diff --git a/Makefile.am b/Makefile.am
index b2abf9b65..57f599f0b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,511 +1,513 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
confignicedir = $(sysconfdir)/build
confignice_SCRIPTS=config.nice
SUBDIRS = po config modules
EXTRA_DIST = UNINSTALL THANKS RELEASE-NOTES configure-tests.py config.nice.in \
config.rpath
# current jsMath version and packages
JSMV = 3.6b
JSMFV = 1.3
JSMATH = jsMath-$(JSMV).zip
JSMATHFONTS = jsMath-fonts-$(JSMFV).zip
# current FCKeditor version
FCKV = 2.6.6
FCKEDITOR = FCKeditor_$(FCKV).zip
# git-version-get stuff:
BUILT_SOURCES = $(top_srcdir)/.version
$(top_srcdir)/.version:
echo $(VERSION) > $@-t && mv $@-t $@
dist-hook:
echo $(VERSION) > $(distdir)/.tarball-version
check-custom-templates:
$(PYTHON) $(top_srcdir)/modules/webstyle/lib/template.py --check-custom-templates $(top_srcdir)
kwalitee-check:
@$(PYTHON) $(top_srcdir)/modules/miscutil/lib/kwalitee.py --stats $(top_srcdir)
kwalitee-check-errors-only:
@$(PYTHON) $(top_srcdir)/modules/miscutil/lib/kwalitee.py --check-errors $(top_srcdir)
kwalitee-check-variables:
@$(PYTHON) $(top_srcdir)/modules/miscutil/lib/kwalitee.py --check-variables $(top_srcdir)
kwalitee-check-indentation:
@$(PYTHON) $(top_srcdir)/modules/miscutil/lib/kwalitee.py --check-indentation $(top_srcdir)
kwalitee-check-sql-queries:
@echo "* Listing potentially dangerous SQL queries:"
@echo "** SQL SELECT queries without explicit column list:"
@find $(top_srcdir) -name '*.py' -exec grep -HEin 'SELECT \* FROM' {} \; 2> /dev/null
@echo "** SQL INSERT queries without explicit column list:"
@find $(top_srcdir) -name '*.py' -exec grep -HEin 'INSERT INTO ([[:alnum:]]|_)+[[:space:]]*VALUES' {} \; 2> /dev/null
@find $(top_srcdir) -name '*.py' -exec grep -HEin 'INSERT INTO ([[:alnum:]]|_)+[[:space:]]*$$' {} \; 2> /dev/null
@echo "** SQL queries using charset-ignorant escape_string():"
@find $(top_srcdir) -name '*.py' -exec grep -HEin 'escape_string' {} \; 2> /dev/null
@echo "** SQL queries using literal '%s':"
@find $(top_srcdir) -name '*.py' -exec grep -HEin "run_sql.*'%[dfis]'" {} \; 2> /dev/null
@find $(top_srcdir) -name '*.py' -exec grep -HEin 'run_sql.*"%[dfis]"' {} \; 2> /dev/null
@echo "** SQL queries with potentially unescaped arguments:"
@find $(top_srcdir) -name '*.py' -exec grep -HEin 'run_sql.* % ' {} \; 2> /dev/null
@echo "* Done."
etags:
\rm -f $(top_srcdir)/TAGS
(cd $(top_srcdir) && find $(top_srcdir) -name "*.py" -print | xargs etags)
install-data-local:
for d in / /cache /log /tmp /data /run ; do \
mkdir -p $(localstatedir)$$d ; \
done
@echo "************************************************************"
@echo "** Invenio software has been successfully installed! **"
@echo "** **"
@echo "** You may proceed to customizing your installation now. **"
@echo "************************************************************"
install-jsmath-plugin:
@echo "***********************************************************"
@echo "** Installing jsMath plugin, please wait... **"
@echo "***********************************************************"
rm -rf /tmp/invenio-jsmath-plugin
mkdir /tmp/invenio-jsmath-plugin
(cd /tmp/invenio-jsmath-plugin && \
wget 'http://downloads.sourceforge.net/jsmath/$(JSMATH)' && \
wget 'http://downloads.sourceforge.net/jsmath/$(JSMATHFONTS)' && \
wget 'http://www.math.union.edu/~dpvc/jsMath/download/extra-fonts/msam10/msam10.zip' && \
wget 'http://www.math.union.edu/~dpvc/jsMath/download/extra-fonts/msbm10/msbm10.zip' && \
unzip -u -d ${prefix}/var/www $(JSMATH) && \
unzip -u -d ${prefix}/var/www $(JSMATHFONTS) && \
unzip -u -d ${prefix}/var/www/jsMath/fonts msam10.zip && \
unzip -u -d ${prefix}/var/www/jsMath/fonts msbm10.zip)
rm -fr /tmp/invenio-jsmath-plugin
@echo "* Installing Invenio-specific jsMath config..."
(cd $(top_srcdir)/modules/webstyle/etc && make install)
@echo "***********************************************************"
@echo "** The jsMath plugin was successfully installed. **"
@echo "** Please do not forget to properly set the option **"
@echo "** CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS in invenio.conf. **"
@echo "***********************************************************"
uninstall-jsmath-plugin:
@rm -rvf ${prefix}/var/www/jsMath
@echo "***********************************************************"
@echo "** The jsMath plugin was successfully uninstalled. **"
@echo "***********************************************************"
install-jscalendar-plugin:
@echo "***********************************************************"
@echo "** Installing jsCalendar plugin, please wait... **"
@echo "***********************************************************"
rm -rf /tmp/invenio-jscalendar-plugin
mkdir /tmp/invenio-jscalendar-plugin
(cd /tmp/invenio-jscalendar-plugin && \
wget 'http://www.dynarch.com/static/jscalendar-1.0.zip' && \
unzip -u jscalendar-1.0.zip && \
mkdir -p ${prefix}/var/www/jsCalendar && \
cp jscalendar-1.0/img.gif ${prefix}/var/www/jsCalendar/jsCalendar.gif && \
cp jscalendar-1.0/calendar.js ${prefix}/var/www/jsCalendar/ && \
cp jscalendar-1.0/calendar-setup.js ${prefix}/var/www/jsCalendar/ && \
cp jscalendar-1.0/lang/calendar-en.js ${prefix}/var/www/jsCalendar/ && \
cp jscalendar-1.0/calendar-blue.css ${prefix}/var/www/jsCalendar/)
rm -fr /tmp/invenio-jscalendar-plugin
@echo "***********************************************************"
@echo "** The jsCalendar plugin was successfully installed. **"
@echo "***********************************************************"
uninstall-jscalendar-plugin:
@rm -rvf ${prefix}/var/www/jsCalendar
@echo "***********************************************************"
@echo "** The jsCalendar plugin was successfully uninstalled. **"
@echo "***********************************************************"
install-jquery-plugins: install-jquery-plugins
@echo "***********************************************************"
@echo "** Installing various jQuery plugins, please wait... **"
@echo "***********************************************************"
mkdir -p ${prefix}/var/www/js
(cd ${prefix}/var/www/js && \
wget http://jqueryjs.googlecode.com/files/jquery-1.3.1.min.js && \
mv jquery-1.3.1.min.js jquery.min.js && \
wget http://jquery-ui.googlecode.com/svn/tags/latest/ui/minified/jquery.effects.core.min.js && \
wget http://jquery-ui.googlecode.com/svn/tags/latest/ui/minified/jquery.effects.highlight.min.js && \
wget http://jquery-ui.googlecode.com/svn/tags/1.7.3/ui/minified/ui.slider.min.js && \
wget http://jquery-ui.googlecode.com/svn/tags/1.7.3/ui/minified/ui.sortable.min.js && \
wget http://www.appelsiini.net/download/jquery.jeditable.mini.js && \
wget http://github.com/malsup/form/raw/master/jquery.form.js --no-check-certificate && \
wget http://jquery-multifile-plugin.googlecode.com/svn/trunk/jquery.MultiFile.pack.js && \
wget http://plugins.jquery.com/files/jquery.autogrow-1.2.2.zip && \
wget http://tablesorter.com/jquery.tablesorter.zip && \
wget http://www.uploadify.com/wp-content/uploads/Uploadify-v2.1.3.zip -O uploadify.zip && \
unzip jquery.tablesorter.zip && \
rm jquery.tablesorter.zip && \
unzip -u uploadify.zip && \
mv -T jquery.uploadify-v2.1.3 uploadify && \
mv uploadify/swfobject.js ./ && \
mv uploadify/cancel.png uploadify/uploadify.css uploadify/uploadify.allglyphs.swf uploadify/uploadify.fla uploadify/uploadify.swf ../img/ && \
mv uploadify/jquery.uploadify.v2.1.3.min.js ./jquery.uploadify.min.js && \
rm uploadify.zip && rm -r uploadify && \
unzip jquery.autogrow-1.2.2.zip jquery.autogrow.js && \
rm jquery.autogrow-1.2.2.zip && \
wget -O json2.js.tmp http://json.org/json2.js && \
grep -v 'alert.*IMPORTANT: Remove this line' json2.js.tmp > json2.js && \
rm json2.js.tmp && \
wget http://jquery-ui.googlecode.com/svn/tags/1.7.3/ui/minified/ui.datepicker.min.js && \
wget -O jquery.hotkeys.min.js http://js-hotkeys.googlecode.com/files/jquery.hotkeys-0.7.8-packed.js && \
wget http://plugins.jquery.com/files/jquery.treeview_3.zip && \
unzip jquery.treeview_3.zip && \
rm jquery.treeview_3.zip && \
wget http://plugins.jquery.com/files/jquery.ajaxPager.js_5.txt && \
mv jquery.ajaxPager.js_5.txt jquery.ajaxPager.js && \
wget http://jqueryui.com/download/jquery-ui-1.7.3.custom.zip && \
unzip jquery-ui-1.7.3.custom.zip development-bundle/ui/ui.core.js && \
mv development-bundle/ui/ui.core.js ui.core.js && \
rm -rf development-bundle && \
rm jquery-ui-1.7.3.custom.zip)
mkdir -p ${prefix}/var/www/img && \
(cd ${prefix}/var/www/img && \
wget -r -np -nH --cut-dirs=4 -A "png,css" -P jquery-ui/themes http://jquery-ui.googlecode.com/svn/tags/1.7.3/themes/base/ && \
wget http://jquery-ui.googlecode.com/svn/tags/1.7.3/themes/redmond/jquery-ui.css && \
wget http://jquery-ui.googlecode.com/svn/tags/1.7.3/demos/images/calendar.gif && \
wget -r -np -nH --cut-dirs=5 -A "png" http://jquery-ui.googlecode.com/svn/tags/1.7.3/themes/redmond/images/)
@echo "***********************************************************"
@echo "** The jQuery plugins were successfully installed. **"
@echo "***********************************************************"
uninstall-jquery-plugins:
(cd ${prefix}/var/www/js && \
rm -f jquery.min.js && \
rm -f jquery.effects.core.min.js && \
rm -f jquery.effects.highlight.min.js && \
rm -f jquery.MultiFile.pack.js && \
rm -f jquery.jeditable.mini.js && \
rm -f jquery.tablesorter.js && \
rm -f jquery.tablesorter.pager.js && \
rm -f ui.datepicker.min.js && \
rm -f jquery.autogrow.js && \
rm -f json2.js && \
rm -f jquery.uploadify.min.js && \
rm -f jquery.ui.slider.min.js && \
rm -f jquery.ui.sortable.min.js && \
rm -rf tablesorter && \
rm -f jquery.hotkeys.min.js && \
rm -rf jquery-treeview && \
rm -f jquery.ajaxPager.js && \
rm -f jquery.form.js && \
rm -f ui.core.js) && \
(cd ${prefix}/var/www/img && \
rm -f cancel.png uploadify.css uploadify.swf uploadify.allglyphs.swf uploadify.fla)
@echo "***********************************************************"
@echo "** The jquery plugins were successfully uninstalled. **"
@echo "***********************************************************"
install-fckeditor-plugin:
@echo "***********************************************************"
@echo "** Installing FCKeditor plugin, please wait... **"
@echo "***********************************************************"
rm -rf ${prefix}/lib/python/invenio/fckeditor/
rm -rf /tmp/invenio-fckeditor-plugin
mkdir /tmp/invenio-fckeditor-plugin
(cd /tmp/invenio-fckeditor-plugin && \
wget 'http://downloads.sourceforge.net/fckeditor/$(FCKEDITOR)' && \
unzip -u -d ${prefix}/var/www $(FCKEDITOR)) && \
mkdir -p ${prefix}/lib/python/invenio/fckeditor/editor/filemanager/connectors/py && \
mv -f ${prefix}/var/www/fckeditor/fckeditor.py ${prefix}/lib/python/invenio/fckeditor/ && \
mv -f ${prefix}/var/www/fckeditor/editor/filemanager/connectors/py/*.py ${prefix}/lib/python/invenio/fckeditor/editor/filemanager/connectors/py/ && \
rm -f ${prefix}/var/www/fckeditor/editor/filemanager/connectors/py/upload.py && \
rm -f ${prefix}/var/www/fckeditor/editor/filemanager/connectors/py/zope.py && \
find ${prefix}/lib/python/invenio/fckeditor -type d -exec touch {}/__init__.py \; && \
find ${prefix}/var/www/fckeditor/ -depth -name '_*' -exec rm -rf {} \; && \
rm -r ${prefix}/var/www/fckeditor/editor/filemanager/connectors && \
find ${prefix}/var/www/fckeditor/fckeditor* -maxdepth 0 ! -name "fckeditor.js" -exec rm -r {} \; && \
rm -fr /tmp/invenio-fckeditor-plugin
@echo "* Installing Invenio-specific FCKeditor config..."
(cd $(top_srcdir)/modules/webstyle/etc && make install)
@echo "***********************************************************"
@echo "** The FCKeditor plugin was successfully installed. **"
@echo "** Please do not forget to properly set the option **"
@echo "** CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR in invenio.conf. **"
@echo "***********************************************************"
uninstall-fckeditor-plugin:
@rm -rvf ${prefix}/var/www/fckeditor
@rm -rvf ${prefix}/lib/python/invenio/fckeditor
@echo "***********************************************************"
@echo "** The FCKeditor plugin was successfully uninstalled. **"
@echo "***********************************************************"
install-pdfa-helper-files:
@echo "***********************************************************"
@echo "** Installing PDF/A helper files, please wait... **"
@echo "***********************************************************"
wget 'http://cdsware.cern.ch/download/invenio-demo-site-files/ISOCoatedsb.icc' -O ${prefix}/etc/websubmit/file_converter_templates/ISOCoatedsb.icc
@echo "***********************************************************"
@echo "** The PDF/A helper files were successfully installed. **"
@echo "***********************************************************"
uninstall-pdfa-helper-files:
rm -f ${prefix}/etc/websubmit/file_converter_templates/ISOCoatedsb.icc
@echo "***********************************************************"
@echo "** The PDF/A helper files were successfully uninstalled. **"
@echo "***********************************************************"
update-v0.3.0-tables update-v0.3.1-tables:
echo "ALTER TABLE idxINDEXNAME CHANGE id_idxINDEX id_idxINDEX mediumint(9) unsigned NOT NULL FIRST;" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkMETHODNAME CHANGE id_rnkMETHOD id_rnkMETHOD mediumint(9) unsigned NOT NULL FIRST;" | ${prefix}/bin/dbexec
echo "ALTER TABLE collectionname CHANGE id_collection id_collection mediumint(9) unsigned NOT NULL FIRST;" | ${prefix}/bin/dbexec
echo "ALTER TABLE formatname CHANGE id_format id_format mediumint(9) unsigned NOT NULL FIRST;" | ${prefix}/bin/dbexec
echo "ALTER TABLE fieldname CHANGE id_field id_field mediumint(9) unsigned NOT NULL FIRST;" | ${prefix}/bin/dbexec
echo "INSERT INTO accACTION (id,name,description,allowedkeywords,optional) VALUES (NULL,'runbibrank','run BibRank','','no');" | ${prefix}/bin/dbexec
echo "INSERT INTO accACTION (id,name,description,allowedkeywords,optional) VALUES (NULL,'cfgbibrank','configure BibRank','','no');" | ${prefix}/bin/dbexec
update-v0.3.2-tables:
echo "ALTER TABLE sbmCOLLECTION_sbmDOCTYPE CHANGE id_son id_son char(10) NOT NULL default '0';" | ${prefix}/bin/dbexec
update-v0.3.3-tables:
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "ALTER TABLE flxLINKTYPEPARAMS CHANGE pname pname varchar(78) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkMETHOD DROP star_category_ranges;" | ${prefix}/bin/dbexec
echo "DROP TABLE rnkSET;" | ${prefix}/bin/dbexec
echo "ALTER TABLE schTASK CHANGE arguments arguments LONGTEXT;" | ${prefix}/bin/dbexec
echo "ALTER TABLE schTASK CHANGE status status varchar(50);" | ${prefix}/bin/dbexec
update-v0.5.0-tables:
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "ALTER TABLE session ADD INDEX uid (uid);" | ${prefix}/bin/dbexec
echo "UPDATE idxINDEXNAME SET ln='cs' WHERE ln='cz';" | ${prefix}/bin/dbexec
echo "UPDATE rnkMETHODNAME SET ln='cs' WHERE ln='cz';" | ${prefix}/bin/dbexec
echo "UPDATE collectionname SET ln='cs' WHERE ln='cz';" | ${prefix}/bin/dbexec
echo "UPDATE collection_portalbox SET ln='cs' WHERE ln='cz';" | ${prefix}/bin/dbexec
echo "UPDATE formatname SET ln='cs' WHERE ln='cz';" | ${prefix}/bin/dbexec
echo "UPDATE fieldname SET ln='cs' WHERE ln='cz';" | ${prefix}/bin/dbexec
echo "UPDATE idxINDEXNAME SET ln='sv' WHERE ln='se';" | ${prefix}/bin/dbexec
echo "UPDATE rnkMETHODNAME SET ln='sv' WHERE ln='se';" | ${prefix}/bin/dbexec
echo "UPDATE collectionname SET ln='sv' WHERE ln='se';" | ${prefix}/bin/dbexec
echo "UPDATE collection_portalbox SET ln='sv' WHERE ln='se';" | ${prefix}/bin/dbexec
echo "UPDATE formatname SET ln='sv' WHERE ln='se';" | ${prefix}/bin/dbexec
echo "UPDATE fieldname SET ln='sv' WHERE ln='se';" | ${prefix}/bin/dbexec
update-v0.7.1-tables:
echo "DROP TABLE oaiHARVEST;" | ${prefix}/bin/dbexec
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "INSERT INTO accACTION (id,name,description,allowedkeywords,optional) VALUES (NULL,'cfgbibharvest','configure BibHarvest','','no');" | ${prefix}/bin/dbexec
echo "INSERT INTO accACTION (id,name,description,allowedkeywords,optional) VALUES (NULL,'runoaiharvest','run BibHarvest oaiharvest','','no');" | ${prefix}/bin/dbexec
echo "INSERT INTO accACTION (id,name,description,allowedkeywords,optional) VALUES (NULL,'cfgwebcomment','configure WebComment','','no');" | ${prefix}/bin/dbexec
echo "INSERT INTO accACTION (id,name,description,allowedkeywords,optional) VALUES (NULL,'runoaiarchive','run BibHarvest oaiarchive','','no');" | ${prefix}/bin/dbexec
echo "INSERT INTO accACTION (id,name,description,allowedkeywords,optional) VALUES (NULL,'runbibedit','run BibEdit','','no');" | ${prefix}/bin/dbexec
echo "ALTER TABLE user ADD nickname varchar(255) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE user ADD last_login datetime NOT NULL default '0000-00-00 00:00:00';" | ${prefix}/bin/dbexec
echo "ALTER TABLE user ADD INDEX nickname (nickname);" | ${prefix}/bin/dbexec
echo "ALTER TABLE sbmFIELD CHANGE subname subname varchar(13) default NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE user_query_basket CHANGE alert_name alert_name varchar(30) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "TRUNCATE TABLE session;" | ${prefix}/bin/dbexec
@echo "**********************************************************"
@echo "** Do not forget to run the basket migration now: **"
@echo "** @PYTHON@ modules/webbasket/lib/webbasket_migration_kit.py "
@echo "** Please see the RELEASE-NOTES for details. **"
@echo "**********************************************************"
@echo "INSERT INTO oaiARCHIVE (id, setName, setSpec, setDescription, setDefinition, setRecList) SELECT id, setName, setSpec, CONCAT_WS('', setDescription), setDefinition, setRecList FROM oaiSET;"
update-v0.90.0-tables:
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "ALTER TABLE format ADD COLUMN (description varchar(255) default '');" | ${prefix}/bin/dbexec
echo "ALTER TABLE format ADD COLUMN (content_type varchar(255) default '');" | ${prefix}/bin/dbexec
update-v0.90.1-tables:
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "ALTER TABLE schTASK ADD INDEX status (status);" | ${prefix}/bin/dbexec
echo "ALTER TABLE schTASK ADD INDEX runtime (runtime);" | ${prefix}/bin/dbexec
echo "ALTER TABLE sbmCATEGORIES ADD COLUMN score TINYINT UNSIGNED NOT NULL DEFAULT 0;" | ${prefix}/bin/dbexec
echo "ALTER TABLE sbmCATEGORIES ADD PRIMARY KEY (doctype, sname);" | ${prefix}/bin/dbexec
echo "ALTER TABLE sbmCATEGORIES ADD KEY doctype (doctype);" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiHARVEST ADD COLUMN setspecs TEXT NOT NULL DEFAULT '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE setDescription setDescription text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE p1 p1 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE f1 f1 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE m1 m1 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE p2 p2 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE f2 f2 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE m2 m2 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE p3 p3 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE f3 f3 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiARCHIVE CHANGE m3 m3 text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "UPDATE bibdoc SET status=0 WHERE status='';" | ${prefix}/bin/dbexec
echo "UPDATE bibdoc SET status=1 WHERE status='deleted';" | ${prefix}/bin/dbexec
echo "ALTER TABLE fmtKNOWLEDGEBASES add COLUMN kbtype char default NULL;" | ${prefix}/bin/dbexec
update-v0.92.0-tables:
echo "UPDATE bibdoc SET status=0 WHERE status='';" | ${prefix}/bin/dbexec
echo "UPDATE bibdoc SET status=1 WHERE status='deleted';" | ${prefix}/bin/dbexec
echo "ALTER TABLE schTASK CHANGE arguments arguments mediumblob;" | ${prefix}/bin/dbexec
echo "UPDATE user SET note=1 WHERE nickname='admin' AND note IS NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE usergroup CHANGE name name varchar(255) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE usergroup ADD login_method varchar(255) NOT NULL default 'INTERNAL';" | ${prefix}/bin/dbexec
echo "ALTER TABLE usergroup ADD UNIQUE KEY login_method_name (login_method(70), name);" | ${prefix}/bin/dbexec
echo "ALTER TABLE user CHANGE settings settings blob default NULL;" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmALLFUNCDESCR VALUES ('Get_Recid', 'This function gets the recid for a document with a given report-number (as stored in the global variable rn).');" | ${prefix}/bin/dbexec
update-v0.92.1-tables:
echo "DROP TABLE rnkCITATIONDATA;" | ${prefix}/bin/dbexec
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "UPDATE bibdoc SET status='DELETED' WHERE status='1';" | ${prefix}/bin/dbexec
echo "UPDATE bibdoc SET status='' WHERE status='0';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibrec ADD KEY creation_date (creation_date);" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibrec ADD KEY modification_date (modification_date);" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibdoc ADD KEY creation_date (creation_date);" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibdoc ADD KEY modification_date (modification_date);" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibdoc ADD KEY docname (docname);" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiHARVEST CHANGE postprocess postprocess varchar(20) NOT NULL default 'h';" | ${prefix}/bin/dbexec
echo "ALTER TABLE oaiHARVEST ADD COLUMN bibfilterprogram varchar(255) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE idxINDEXNAME CHANGE ln ln char(5) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE idxINDEX ADD COLUMN stemming_language VARCHAR(10) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkMETHODNAME CHANGE ln ln char(5) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkDOWNLOADS CHANGE id_bibdoc id_bibdoc mediumint(9) unsigned default NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkDOWNLOADS CHANGE file_format file_format varchar(10) NULL default NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE collectionname CHANGE ln ln char(5) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE collection_portalbox CHANGE ln ln char(5) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE format ADD COLUMN visibility TINYINT NOT NULL default 1;" | ${prefix}/bin/dbexec
echo "ALTER TABLE formatname CHANGE ln ln char(5) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE fieldname CHANGE ln ln char(5) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE accROLE ADD COLUMN firerole_def_ser blob NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE accROLE ADD COLUMN firerole_def_src text NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE user_accROLE ADD COLUMN expiration datetime NOT NULL default '9999-12-31 23:59:59';" | ${prefix}/bin/dbexec
echo "ALTER TABLE user DROP INDEX id, ADD PRIMARY KEY id (id);" | ${prefix}/bin/dbexec
echo -e 'from invenio.dbquery import run_sql;\
map(lambda index_id: run_sql("ALTER TABLE idxPHRASE%02dF CHANGE term term TEXT NULL DEFAULT NULL, DROP INDEX term, ADD INDEX term (term (50))" % index_id[0]), run_sql("select id from idxINDEX"))' | $(PYTHON)
echo "INSERT INTO rnkCITATIONDATA VALUES (1,'citationdict','','');" | ${prefix}/bin/dbexec
echo "INSERT INTO rnkCITATIONDATA VALUES (2,'reversedict','','');" | ${prefix}/bin/dbexec
echo "INSERT INTO rnkCITATIONDATA VALUES (3,'selfcitdict','','');" | ${prefix}/bin/dbexec
update-v0.99.0-tables:
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "ALTER TABLE bibdoc ADD COLUMN more_info mediumblob NULL default NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE schTASK ADD COLUMN priority tinyint(4) NOT NULL default 0;" | ${prefix}/bin/dbexec
echo "ALTER TABLE schTASK ADD KEY priority (priority);" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkCITATIONDATA DROP PRIMARY KEY;" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkCITATIONDATA ADD PRIMARY KEY (id);" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkCITATIONDATA CHANGE id id mediumint(8) unsigned NOT NULL auto_increment;" | ${prefix}/bin/dbexec
echo "ALTER TABLE rnkCITATIONDATA ADD UNIQUE KEY object_name (object_name);" | ${prefix}/bin/dbexec
echo "ALTER TABLE sbmPARAMETERS CHANGE value value text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE sbmAPPROVAL ADD note text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE hstDOCUMENT CHANGE docsize docsize bigint(15) unsigned NOT NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtACTIONHISTORY CHANGE client_host client_host int(10) unsigned default NULL;" | ${prefix}/bin/dbexec
update-v0.99.1-tables:
@echo "Nothing to do; table structure did not change between v0.99.1 and v0.99.2."
update-v0.99.2-tables:
echo "RENAME TABLE oaiARCHIVE TO oaiREPOSITORY;" | ${prefix}/bin/dbexec
${prefix}/bin/dbexec < $(top_srcdir)/modules/miscutil/sql/tabcreate.sql
echo "INSERT INTO knwKB (id,name,description,kbtype) SELECT id,name,description,'' FROM fmtKNOWLEDGEBASES;" | ${prefix}/bin/dbexec
echo "INSERT INTO knwKBRVAL (id,m_key,m_value,id_knwKB) SELECT id,m_key,m_value,id_fmtKNOWLEDGEBASES FROM fmtKNOWLEDGEBASEMAPPINGS;" | ${prefix}/bin/dbexec
echo "ALTER TABLE sbmPARAMETERS CHANGE name name varchar(40) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibdoc CHANGE docname docname varchar(250) COLLATE utf8_bin NOT NULL default 'file';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibdoc CHANGE status status text NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bibdoc ADD COLUMN text_extraction_date datetime NOT NULL default '0000-00-00';" | ${prefix}/bin/dbexec
echo "ALTER TABLE collection DROP COLUMN restricted;" | ${prefix}/bin/dbexec
echo "ALTER TABLE schTASK CHANGE host host varchar(255) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE hstTASK CHANGE host host varchar(255) NOT NULL default '';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bib85x DROP INDEX kv, ADD INDEX kv (value(100));" | ${prefix}/bin/dbexec
echo "UPDATE clsMETHOD SET location='http://cdsware.cern.ch/download/invenio-demo-site-files/HEP.rdf' WHERE name='HEP' AND location='';" | ${prefix}/bin/dbexec
echo "UPDATE clsMETHOD SET location='http://cdsware.cern.ch/download/invenio-demo-site-files/NASA-subjects.rdf' WHERE name='NASA-subjects' AND location='';" | ${prefix}/bin/dbexec
echo "UPDATE accACTION SET name='runoairepository', description='run oairepositoryupdater task' WHERE name='runoaiarchive';" | ${prefix}/bin/dbexec
echo "UPDATE accACTION SET name='cfgoaiharvest', description='configure OAI Harvest' WHERE name='cfgbibharvest';" | ${prefix}/bin/dbexec
echo "ALTER TABLE accARGUMENT CHANGE value value varchar(255);" | ${prefix}/bin/dbexec
echo "UPDATE accACTION SET allowedkeywords='doctype,act,categ' WHERE name='submit';" | ${prefix}/bin/dbexec
echo "INSERT INTO accARGUMENT(keyword,value) VALUES ('categ','*');" | ${prefix}/bin/dbexec
echo "INSERT INTO accROLE_accACTION_accARGUMENT(id_accROLE,id_accACTION,id_accARGUMENT,argumentlistid) SELECT DISTINCT raa.id_accROLE,raa.id_accACTION,accARGUMENT.id,raa.argumentlistid FROM accROLE_accACTION_accARGUMENT as raa JOIN accACTION on id_accACTION=accACTION.id,accARGUMENT WHERE accACTION.name='submit' and accARGUMENT.keyword='categ' and accARGUMENT.value='*';" | ${prefix}/bin/dbexec
echo "UPDATE accACTION SET allowedkeywords='name,with_editor_rights' WHERE name='cfgwebjournal';" | ${prefix}/bin/dbexec
echo "INSERT INTO accARGUMENT(keyword,value) VALUES ('with_editor_rights','yes');" | ${prefix}/bin/dbexec
echo "INSERT INTO accROLE_accACTION_accARGUMENT(id_accROLE,id_accACTION,id_accARGUMENT,argumentlistid) SELECT DISTINCT raa.id_accROLE,raa.id_accACTION,accARGUMENT.id,raa.argumentlistid FROM accROLE_accACTION_accARGUMENT as raa JOIN accACTION on id_accACTION=accACTION.id,accARGUMENT WHERE accACTION.name='cfgwebjournal' and accARGUMENT.keyword='with_editor_rights' and accARGUMENT.value='yes';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskEXTREC CHANGE id id int(15) unsigned NOT NULL auto_increment;" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskEXTREC ADD external_id int(15) NOT NULL default '0';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskEXTREC ADD collection_id int(15) unsigned NOT NULL default '0';" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskEXTREC ADD original_url text;" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD status char(2) NOT NULL default 'ok';" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD KEY status (status);" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmALLFUNCDESCR VALUES ('Move_Photos_to_Storage','Attach/edit the pictures uploaded with the \"create_photos_manager_interface()\" function');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFIELDDESC VALUES ('Upload_Photos',NULL,'','R',NULL,NULL,NULL,NULL,NULL,'\"\"\"\r\nThis is an example of element that creates a photos upload interface.\r\nClone it, customize it and integrate it into your submission. Then add function \r\n\'Move_Photos_to_Storage\' to your submission functions list, in order for files \r\nuploaded with this interface to be attached to the record. More information in \r\nthe WebSubmit admin guide.\r\n\"\"\"\r\n\r\nfrom invenio.websubmit_functions.ParamFile import ParamFromFile\r\nfrom invenio.websubmit_functions.Move_Photos_to_Storage import read_param_file, create_photos_manager_interface, get_session_id\r\n\r\n# Retrieve session id\r\ntry:\r\n # User info is defined only in MBI/MPI actions...\r\n session_id = get_session_id(None, uid, user_info) \r\nexcept:\r\n session_id = get_session_id(req, uid, {})\r\n\r\n# Retrieve context\r\nindir = curdir.split(\'/\')[-3]\r\ndoctype = curdir.split(\'/\')[-2]\r\naccess = curdir.split(\'/\')[-1]\r\n\r\n# Get the record ID, if any\r\nsysno = ParamFromFile(\"%s/%s\" % (curdir,\'SN\')).strip()\r\n\r\n\"\"\"\r\nModify below the configuration of the photos manager interface.\r\nNote: \'can_reorder_photos\' parameter is not yet fully taken into consideration\r\n\r\nDocumentation of the function is available by running:\r\necho -e \'from invenio.websubmit_functions.Move_Photos_to_Storage import create_photos_manager_interface as f\\nprint f.__doc__\' | python\r\n\"\"\"\r\ntext += create_photos_manager_interface(sysno, session_id, uid,\r\n doctype, indir, curdir, access,\r\n can_delete_photos=True,\r\n can_reorder_photos=True,\r\n can_upload_photos=True,\r\n editor_width=700,\r\n editor_height=400,\r\n initial_slider_value=100,\r\n max_slider_value=200,\r\n min_slider_value=80)','0000-00-00','0000-00-00',NULL,NULL,0);" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Photos_to_Storage','iconsize');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFIELDDESC VALUES ('Upload_Files',NULL,'','R',NULL,NULL,NULL,NULL,NULL,'\"\"\"\r\nThis is an example of element that creates a file upload interface.\r\nClone it, customize it and integrate it into your submission. Then add function \r\n\'Move_Uploaded_Files_to_Storage\' to your submission functions list, in order for files \r\nuploaded with this interface to be attached to the record. More information in \r\nthe WebSubmit admin guide.\r\n\"\"\"\r\nfrom invenio.websubmit_managedocfiles import create_file_upload_interface\r\nfrom invenio.websubmit_functions.Shared_Functions import ParamFromFile\r\n\r\nindir = ParamFromFile(os.path.join(curdir, \'indir\'))\r\ndoctype = ParamFromFile(os.path.join(curdir, \'doctype\'))\r\naccess = ParamFromFile(os.path.join(curdir, \'access\'))\r\ntry:\r\n sysno = int(ParamFromFile(os.path.join(curdir, \'SN\')).strip())\r\nexcept:\r\n sysno = -1\r\nln = ParamFromFile(os.path.join(curdir, \'ln\'))\r\n\r\n\"\"\"\r\nRun the following to get the list of parameters of function \'create_file_upload_interface\':\r\necho -e \'from invenio.websubmit_managedocfiles import create_file_upload_interface as f\\nprint f.__doc__\' | python\r\n\"\"\"\r\ntext = create_file_upload_interface(recid=sysno,\r\n print_outside_form_tag=False,\r\n include_headers=True,\r\n ln=ln,\r\n doctypes_and_desc=[(\'main\',\'Main document\'),\r\n (\'additional\',\'Figure, schema, etc.\')],\r\n can_revise_doctypes=[\'*\'],\r\n can_describe_doctypes=[\'main\'],\r\n can_delete_doctypes=[\'additional\'],\r\n can_rename_doctypes=[\'main\'],\r\n sbm_indir=indir, sbm_doctype=doctype, sbm_access=access)[1]\r\n','0000-00-00','0000-00-00',NULL,NULL,0);" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Uploaded_Files_to_Storage','forceFileRevision');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmALLFUNCDESCR VALUES ('Create_Upload_Files_Interface','Display generic interface to add/revise/delete files. To be used before function \"Move_Uploaded_Files_to_Storage\"');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmALLFUNCDESCR VALUES ('Move_Uploaded_Files_to_Storage','Attach files uploaded with \"Create_Upload_Files_Interface\"')" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Revised_Files_to_Storage','elementNameToDoctype');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Revised_Files_to_Storage','createIconDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Revised_Files_to_Storage','createRelatedFormats');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Revised_Files_to_Storage','iconsize');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Revised_Files_to_Storage','keepPreviousVersionDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmALLFUNCDESCR VALUES ('Move_Revised_Files_to_Storage','Revise files initially uploaded with \"Move_Files_to_Storage\"')" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','maxsize');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','minsize');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','doctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','restrictions');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canDeleteDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canReviseDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canDescribeDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canCommentDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canKeepDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canAddFormatDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canRestrictDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canRenameDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','canNameNewFiles');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','createRelatedFormats');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','keepDefault');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','showLinks');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','fileLabel');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','filenameLabel');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','descriptionLabel');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','commentLabel');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','restrictionLabel');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','startDoc');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','endDoc');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','defaultFilenameDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Create_Upload_Files_Interface','maxFilesDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Uploaded_Files_to_Storage','iconsize');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Uploaded_Files_to_Storage','createIconDoctypes');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Report_Number_Generation','nblength');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Second_Report_Number_Generation','2nd_nb_length');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmALLFUNCDESCR VALUES ('Move_FCKeditor_Files_to_Storage','Transfer files attached to the record with the FCKeditor');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_FCKeditor_Files_to_Storage','input_fields');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Stamp_Uploaded_Files','layer');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Stamp_Replace_Single_File_Approval','layer');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Stamp_Replace_Single_File_Approval','switch_file');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Stamp_Uploaded_Files','switch_file');" | ${prefix}/bin/dbexec
echo "INSERT INTO sbmFUNDESC VALUES ('Move_Files_to_Storage','paths_and_restrictions');" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD round_name varchar(255) NOT NULL default ''" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD restriction varchar(50) NOT NULL default ''" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD in_reply_to_id_cmtRECORDCOMMENT int(15) unsigned NOT NULL default '0'" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD KEY in_reply_to_id_cmtRECORDCOMMENT (in_reply_to_id_cmtRECORDCOMMENT);" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskRECORDCOMMENT ADD in_reply_to_id_bskRECORDCOMMENT int(15) unsigned NOT NULL default '0'" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskRECORDCOMMENT ADD KEY in_reply_to_id_bskRECORDCOMMENT (in_reply_to_id_bskRECORDCOMMENT);" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD reply_order_cached_data blob NULL default NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskRECORDCOMMENT ADD reply_order_cached_data blob NULL default NULL;" | ${prefix}/bin/dbexec
echo "ALTER TABLE cmtRECORDCOMMENT ADD INDEX (reply_order_cached_data(40));" | ${prefix}/bin/dbexec
echo "ALTER TABLE bskRECORDCOMMENT ADD INDEX (reply_order_cached_data(40));" | ${prefix}/bin/dbexec
echo -e 'from invenio.webcommentadminlib import migrate_comments_populate_threads_index;\
migrate_comments_populate_threads_index()' | $(PYTHON)
+ echo -e 'from invenio.access_control_firerole import repair_role_definitions;\
+ repair_role_definitions()' | $(PYTHON)
CLEANFILES = *~ *.pyc *.tmp
diff --git a/config/invenio.conf b/config/invenio.conf
index fe4bd38c5..2e85eaa09 100644
--- a/config/invenio.conf
+++ b/config/invenio.conf
@@ -1,1164 +1,1168 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
###################################################
## About 'invenio.conf' and 'invenio-local.conf' ##
###################################################
## The 'invenio.conf' file contains the vanilla default configuration
## parameters of a Invenio installation, as coming out of the
## distribution. The file should be self-explanatory. Once installed
## in its usual location (usually /opt/invenio/etc), you could in
## principle go ahead and change the values according to your local
## needs, but this is not advised.
##
## If you would like to customize some of these parameters, you should
## rather create a file named 'invenio-local.conf' in the same
## directory where 'invenio.conf' lives and you should write there
## only the customizations that you want to be different from the
## vanilla defaults.
##
## Here is a realistic, minimalist, yet production-ready example of
## what you would typically put there:
##
## $ cat /opt/invenio/etc/invenio-local.conf
## [Invenio]
## CFG_SITE_NAME = John Doe's Document Server
## CFG_SITE_NAME_INTL_fr = Serveur des Documents de John Doe
## CFG_SITE_URL = http://your.site.com
## CFG_SITE_SECURE_URL = https://your.site.com
## CFG_SITE_ADMIN_EMAIL = john.doe@your.site.com
## CFG_SITE_SUPPORT_EMAIL = john.doe@your.site.com
## CFG_WEBALERT_ALERT_ENGINE_EMAIL = john.doe@your.site.com
## CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL = john.doe@your.site.com
## CFG_WEBCOMMENT_DEFAULT_MODERATOR = john.doe@your.site.com
## CFG_DATABASE_HOST = localhost
## CFG_DATABASE_NAME = invenio
## CFG_DATABASE_USER = invenio
## CFG_DATABASE_PASS = my123p$ss
##
## You should override at least the parameters mentioned above and the
## parameters mentioned in the `Part 1: Essential parameters' below in
## order to define some very essential runtime parameters such as the
## name of your document server (CFG_SITE_NAME and
## CFG_SITE_NAME_INTL_*), the visible URL of your document server
## (CFG_SITE_URL and CFG_SITE_SECURE_URL), the email address of the
## local Invenio administrator, comment moderator, and alert engine
## (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_ADMIN_EMAIL, etc), and last but
## not least your database credentials (CFG_DATABASE_*).
##
## The Invenio system will then read both the default invenio.conf
## file and your customized invenio-local.conf file and it will
## override any default options with the ones you have specified in
## your local file. This cascading of configuration parameters will
## ease your future upgrades.
[Invenio]
###################################
## Part 1: Essential parameters ##
###################################
## This part defines essential Invenio internal parameters that
## everybody should override, like the name of the server or the email
## address of the local Invenio administrator.
## CFG_DATABASE_* - specify which MySQL server to use, the name of the
## database to use, and the database access credentials.
CFG_DATABASE_HOST = localhost
CFG_DATABASE_PORT = 3306
CFG_DATABASE_NAME = invenio
CFG_DATABASE_USER = invenio
CFG_DATABASE_PASS = my123p$ss
## CFG_SITE_URL - specify URL under which your installation will be
## visible. For example, use "http://your.site.com". Do not leave
## trailing slash.
CFG_SITE_URL = http://localhost
## CFG_SITE_SECURE_URL - specify secure URL under which your
## installation secure pages such as login or registration will be
## visible. For example, use "https://your.site.com". Do not leave
## trailing slash. If you don't plan on using HTTPS, then you may
## leave this empty.
CFG_SITE_SECURE_URL = https://localhost
## CFG_SITE_NAME -- the visible name of your Invenio installation.
CFG_SITE_NAME = Atlantis Institute of Fictive Science
## CFG_SITE_NAME_INTL -- the international versions of CFG_SITE_NAME
## in various languages. (See also CFG_SITE_LANGS below.)
CFG_SITE_NAME_INTL_en = Atlantis Institute of Fictive Science
CFG_SITE_NAME_INTL_fr = Atlantis Institut des Sciences Fictives
CFG_SITE_NAME_INTL_de = Atlantis Institut der fiktiven Wissenschaft
CFG_SITE_NAME_INTL_es = Atlantis Instituto de la Ciencia Fictive
CFG_SITE_NAME_INTL_ca = Institut Atlantis de Ciència Fictícia
CFG_SITE_NAME_INTL_pt = Instituto Atlantis de Ciência Fictícia
CFG_SITE_NAME_INTL_it = Atlantis Istituto di Scienza Fittizia
CFG_SITE_NAME_INTL_ru = Атлантис Институт фиктивных Наук
CFG_SITE_NAME_INTL_sk = Atlantis Inštitút Fiktívnych Vied
CFG_SITE_NAME_INTL_cs = Atlantis Institut Fiktivních Věd
CFG_SITE_NAME_INTL_no = Atlantis Institutt for Fiktiv Vitenskap
CFG_SITE_NAME_INTL_sv = Atlantis Institut för Fiktiv Vetenskap
CFG_SITE_NAME_INTL_el = Ινστιτούτο Φανταστικών Επιστημών Ατλαντίδος
CFG_SITE_NAME_INTL_uk = Інститут вигаданих наук в Атлантісі
CFG_SITE_NAME_INTL_ja = Fictive 科学のAtlantis の協会
CFG_SITE_NAME_INTL_pl = Instytut Fikcyjnej Nauki Atlantis
CFG_SITE_NAME_INTL_bg = Институт за фиктивни науки Атлантис
CFG_SITE_NAME_INTL_hr = Institut Fiktivnih Znanosti Atlantis
CFG_SITE_NAME_INTL_zh_CN = 阿特兰提斯虚拟科学学院
CFG_SITE_NAME_INTL_zh_TW = 阿特蘭提斯虛擬科學學院
CFG_SITE_NAME_INTL_hu = Kitalált Tudományok Atlantiszi Intézete
CFG_SITE_NAME_INTL_af = Atlantis Instituut van Fiktiewe Wetenskap
CFG_SITE_NAME_INTL_gl = Instituto Atlantis de Ciencia Fictive
CFG_SITE_NAME_INTL_ro = Institutul Atlantis al Ştiinţelor Fictive
CFG_SITE_NAME_INTL_rw = Atlantis Ishuri Rikuru Ry'ubuhanga
CFG_SITE_NAME_INTL_ka = ატლანტიდის ფიქტიური მეცნიერების ინსტიტუტი
## CFG_SITE_LANG -- the default language of the interface: '
CFG_SITE_LANG = en
## CFG_SITE_LANGS -- list of all languages the user interface should
## be available in, separated by commas. The order specified below
## will be respected on the interface pages. A good default would be
## to use the alphabetical order. Currently supported languages
## include Afrikaans, Bulgarian, Catalan, Czech, German, Georgian,
## Greek, English, Spanish, French, Croatian, Hungarian, Galician,
## Italian, Japanese, Kinyarwanda, Norwegian, Polish, Portuguese,
## Romanian, Russian, Slovak, Swedish, Ukrainian, Chinese (China),
## Chinese (Taiwan), so that the eventual maximum you can currently
## select is "af,bg,ca,cs,de,el,en,es,fr,hr,gl,it,ka,rw,hu,ja,no,pl,pt,ro,ru,sk,sv,uk,zh_CN,zh_TW".
CFG_SITE_LANGS = af,bg,ca,cs,de,el,en,es,fr,hr,gl,it,ka,rw,hu,ja,no,pl,pt,ro,ru,sk,sv,uk,zh_CN,zh_TW
## CFG_SITE_SUPPORT_EMAIL -- the email address of the support team for
## this installation:
CFG_SITE_SUPPORT_EMAIL = info@invenio-software.org
## CFG_SITE_ADMIN_EMAIL -- the email address of the 'superuser' for
## this installation. Enter your email address below and login with
## this address when using Invenio inistration modules. You
## will then be automatically recognized as superuser of the system.
CFG_SITE_ADMIN_EMAIL = info@invenio-software.org
## CFG_SITE_EMERGENCY_PHONE_NUMBERS -- list of mobile phone numbers to
## which an sms should be sent in case of emergency (e.g. bibsched queue
## has been stopped because of an error).
## Note that in order to use this function, if CFG_CERN_SITE is set to 0,
## the function send_sms in errorlib should be reimplemented.
CFG_SITE_EMERGENCY_PHONE_NUMBERS =
## CFG_SITE_ADMIN_EMAIL_EXCEPTIONS -- set this to 0 if you do not want
## to receive any captured exception via email to CFG_SITE_ADMIN_EMAIL
## address. Captured exceptions will still be available in
## var/log/invenio.err file. Set this to 1 if you want to receive
## some of the captured exceptions (this depends on the actual place
## where the exception is captured). Set this to 2 if you want to
## receive all captured exceptions.
CFG_SITE_ADMIN_EMAIL_EXCEPTIONS = 1
## CFG_CERN_SITE -- do we want to enable CERN-specific code?
## Put "1" for "yes" and "0" for "no".
CFG_CERN_SITE = 0
## CFG_INSPIRE_SITE -- do we want to enable INSPIRE-specific code?
## Put "1" for "yes" and "0" for "no".
CFG_INSPIRE_SITE = 0
## CFG_ADS_SITE -- do we want to enable ADS-specific code?
## Put "1" for "yes" and "0" for "no".
CFG_ADS_SITE = 0
+## CFG_OPENAIRE_SITE -- do we want to enable OpenAIRE-specific code?
+## Put "1" for "yes" and "0" for "no".
+CFG_OPENAIRE_SITE = 0
+
## CFG_DEVEL_SITE -- is this a development site? If it is, you might
## prefer that it does not do certain things. For example, you might
## not want WebSubmit to send certain emails or trigger certain
## processes on a development site.
## Put "1" for "yes" (this is a development site) or "0" for "no"
## (this isn't a development site.)
CFG_DEVEL_SITE = 0
################################
## Part 2: Web page style ##
################################
## The variables affecting the page style. The most important one is
## the 'template skin' you would like to use and the obfuscation mode
## for your email addresses. Please refer to the WebStyle Admin Guide
## for more explanation. The other variables are listed here mostly
## for backwards compatibility purposes only.
## CFG_WEBSTYLE_TEMPLATE_SKIN -- what template skin do you want to
## use?
CFG_WEBSTYLE_TEMPLATE_SKIN = default
## CFG_WEBSTYLE_EMAIL_ADDRESSES_OBFUSCATION_MODE. How do we "protect"
## email addresses from undesired automated email harvesters? This
## setting will not affect 'support' and 'admin' emails.
## NOTE: there is no ultimate solution to protect against email
## harvesting. All have drawbacks and can more or less be
## circumvented. Choose you preferred mode ([t] means "transparent"
## for the user):
## -1: hide all emails.
## [t] 0 : no protection, email returned as is.
## foo@example.com => foo@example.com
## 1 : basic email munging: replaces @ by [at] and . by [dot]
## foo@example.com => foo [at] example [dot] com
## [t] 2 : transparent name mangling: characters are replaced by
## equivalent HTML entities.
## foo@example.com => foo@example.com
## [t] 3 : javascript insertion. Requires Javascript enabled on client
## side.
## 4 : replaces @ and . characters by gif equivalents.
## foo@example.com => fooexamplecom
CFG_WEBSTYLE_EMAIL_ADDRESSES_OBFUSCATION_MODE = 2
## CFG_WEBSTYLE_INSPECT_TEMPLATES -- Do we want to debug all template
## functions so that they would return HTML results wrapped in
## comments indicating which part of HTML page was created by which
## template function? Useful only for debugging Pythonic HTML
## template. See WebStyle Admin Guide for more information.
CFG_WEBSTYLE_INSPECT_TEMPLATES = 0
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXLEFTTOP -- eventual global HTML
## left top box:
CFG_WEBSTYLE_CDSPAGEBOXLEFTTOP =
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXLEFTBOTTOM -- eventual global
## HTML left bottom box:
CFG_WEBSTYLE_CDSPAGEBOXLEFTBOTTOM =
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXRIGHTTOP -- eventual global
## HTML right top box:
CFG_WEBSTYLE_CDSPAGEBOXRIGHTTOP =
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXRIGHTBOTTOM -- eventual global
## HTML right bottom box:
CFG_WEBSTYLE_CDSPAGEBOXRIGHTBOTTOM =
## CFG_WEBSTYLE_HTTP_STATUS_ALERT_LIST -- when certain HTTP status
## codes are raised to the WSGI handler, the corresponding exceptions
## and error messages can be sent to the system administrator for
## inspecting. This is useful to detect and correct errors. The
## variable represents a comma-separated list of HTTP statuses that
## should alert admin. Wildcards are possible. If the status is
## followed by an "r", it means that a referer is required to exist
## (useful to distinguish broken known links from URL typos when 404
## errors are raised).
CFG_WEBSTYLE_HTTP_STATUS_ALERT_LIST = 404r,400,5*,41*
##################################
## Part 3: WebSearch parameters ##
##################################
## This section contains some configuration parameters for WebSearch
## module. Please note that WebSearch is mostly configured on
## run-time via its WebSearch Admin web interface. The parameters
## below are the ones that you do not probably want to modify very
## often during the runtime. (Note that you may modify them
## afterwards too, though.)
## CFG_WEBSEARCH_SEARCH_CACHE_SIZE -- how many queries we want to
## cache in memory per one Apache httpd process? This cache is used
## mainly for "next/previous page" functionality, but it caches also
## "popular" user queries if more than one user happen to search for
## the same thing. Note that large numbers may lead to great memory
## consumption. We recommend a value not greater than 100.
CFG_WEBSEARCH_SEARCH_CACHE_SIZE = 100
## CFG_WEBSEARCH_FIELDS_CONVERT -- if you migrate from an older
## system, you may want to map field codes of your old system (such as
## 'ti') to Invenio/MySQL ("title"). Use Python dictionary syntax
## for the translation table, e.g. {'wau':'author', 'wti':'title'}.
## Usually you don't want to do that, and you would use empty dict {}.
CFG_WEBSEARCH_FIELDS_CONVERT = {}
## CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH -- width of the
## search pattern window in the light search interface, in
## characters. CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH = 60
CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH = 60
## CFG_WEBSEARCH_SIMPLESEARCH_PATTERN_BOX_WIDTH -- width of the search
## pattern window in the simple search interface, in characters.
CFG_WEBSEARCH_SIMPLESEARCH_PATTERN_BOX_WIDTH = 40
## CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH -- width of the
## search pattern window in the advanced search interface, in
## characters.
CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH = 30
## CFG_WEBSEARCH_NB_RECORDS_TO_SORT -- how many records do we still
## want to sort? For higher numbers we print only a warning and won't
## perform any sorting other than default 'latest records first', as
## sorting would be very time consuming then. We recommend a value of
## not more than a couple of thousands.
CFG_WEBSEARCH_NB_RECORDS_TO_SORT = 1000
## CFG_WEBSEARCH_CALL_BIBFORMAT -- if a record is being displayed but
## it was not preformatted in the "HTML brief" format, do we want to
## call BibFormatting on the fly? Put "1" for "yes" and "0" for "no".
## Note that "1" will display the record exactly as if it were fully
## preformatted, but it may be slow due to on-the-fly processing; "0"
## will display a default format very fast, but it may not have all
## the fields as in the fully preformatted HTML brief format. Note
## also that this option is active only for old (PHP) formats; the new
## (Python) formats are called on the fly by default anyway, since
## they are much faster. When usure, please set "0" here.
CFG_WEBSEARCH_CALL_BIBFORMAT = 0
## CFG_WEBSEARCH_USE_ALEPH_SYSNOS -- do we want to make old SYSNOs
## visible rather than MySQL's record IDs? You may use this if you
## migrate from a different e-doc system, and you store your old
## system numbers into 970__a. Put "1" for "yes" and "0" for
## "no". Usually you don't want to do that, though.
CFG_WEBSEARCH_USE_ALEPH_SYSNOS = 0
## CFG_WEBSEARCH_I18N_LATEST_ADDITIONS -- Put "1" if you want the
## "Latest Additions" in the web collection pages to show
## internationalized records. Useful only if your brief BibFormat
## templates contains internationalized strings. Otherwise put "0" in
## order not to slow down the creation of latest additions by WebColl.
CFG_WEBSEARCH_I18N_LATEST_ADDITIONS = 0
## CFG_WEBSEARCH_INSTANT_BROWSE -- the number of records to display
## under 'Latest Additions' in the web collection pages.
CFG_WEBSEARCH_INSTANT_BROWSE = 10
## CFG_WEBSEARCH_INSTANT_BROWSE_RSS -- the number of records to
## display in the RSS feed.
CFG_WEBSEARCH_INSTANT_BROWSE_RSS = 25
## CFG_WEBSEARCH_RSS_I18N_COLLECTIONS -- comma-separated list of
## collections that feature an internationalized RSS feed on their
## main seach interface page created by webcoll. Other collections
## will have RSS feed using CFG_SITE_LANG.
CFG_WEBSEARCH_RSS_I18N_COLLECTIONS =
## CFG_WEBSEARCH_RSS_TTL -- number of minutes that indicates how long
## a feed cache is valid.
CFG_WEBSEARCH_RSS_TTL = 360
## CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS -- maximum number of request kept
## in cache. If the cache is filled, following request are not cached.
CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS = 1000
## CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD -- up to how many author names
## to print explicitely; for more print "et al". Note that this is
## used in default formatting that is seldomly used, as usually
## BibFormat defines all the format. The value below is only used
## when BibFormat fails, for example.
CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD = 3
## CFG_WEBSEARCH_NARROW_SEARCH_SHOW_GRANDSONS -- whether to show or
## not collection grandsons in Narrow Search boxes (sons are shown by
## default, grandsons are configurable here). Use 0 for no and 1 for
## yes.
CFG_WEBSEARCH_NARROW_SEARCH_SHOW_GRANDSONS = 1
## CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX -- shall we
## create help links for Ellis, Nick or Ellis, Nicholas and friends
## when Ellis, N was searched for? Useful if you have one author
## stored in the database under several name formats, namely surname
## comma firstname and surname comma initial cataloging policy. Use 0
## for no and 1 for yes.
CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX = 1
## CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS -- jsMath is a JavaScript
## library that renders (La)TeX mathematical formulas in the client
## browser. This parameter must contain a comma-separated list of
## output formats for which to apply the jsMath rendering, for example
## "hb,hd". If the list is empty, jsMath is disabled.
CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS =
## CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_TIMEOUT -- when searching
## external collections (e.g. SPIRES, CiteSeer, etc), how many seconds
## do we wait for reply before abandonning?
CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_TIMEOUT = 5
## CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_MAXRESULTS -- how many
## results do we fetch?
CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_MAXRESULTS = 10
## CFG_WEBSEARCH_SPLIT_BY_COLLECTION -- do we want to split the search
## results by collection or not? Use 0 for not, 1 for yes.
CFG_WEBSEARCH_SPLIT_BY_COLLECTION = 1
## CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS -- the default number of
## records to display per page in the search results pages.
CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS = 10
## CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS -- in order to limit denial of
## service attacks the total number of records per group displayed as a
## result of a search query will be limited to this number. Only the superuser
## queries will not be affected by this limit.
CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS = 200
## CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL -- logged in users
## might have rights to access some restricted collections. This variable
## tweaks the kind of support the system will automatically provide to the
## user with respect to searching into these restricted collections.
## Set this to 0 in order to have the user to explicitly activate restricted
## collections in order to search into them. Set this to 1 in order to
## propose to the user the list of restricted collections to which he/she has
## rights (note: this is not yet implemented). Set this to 2 in order to
## silently add all the restricted collections to which the user has rights to
## to any query.
## Note: the system will discover which restricted collections a user has
## rights to, at login time. The time complexity of this procedure is
## proportional to the number of restricted collections. E.g. for a system
## with ~50 restricted collections, you might expect ~1s of delay in the
## login time, when this variable is set to a value higher than 0.
CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL = 0
## CFG_WEBSEARCH_SHOW_COMMENT_COUNT -- do we want to show the 'N comments'
## links on the search engine pages? (useful only when you have allowed
## commenting)
CFG_WEBSEARCH_SHOW_COMMENT_COUNT = 1
## CFG_WEBSEARCH_SHOW_REVIEW_COUNT -- do we want to show the 'N reviews'
## links on the search engine pages? (useful only when you have allowed
## reviewing)
CFG_WEBSEARCH_SHOW_REVIEW_COUNT = 1
## CFG_WEBSEARCH_FULLTEXT_SNIPPETS -- how many full-text snippets to
## display for full-text searches?
CFG_WEBSEARCH_FULLTEXT_SNIPPETS = 0
## CFG_WEBSEARCH_FULLTEXT_SNIPPETS_WORDS -- how many context words
## to display around the pattern in the snippet?
CFG_WEBSEARCH_FULLTEXT_SNIPPETS_WORDS = 4
#######################################
## Part 4: BibHarvest OAI parameters ##
#######################################
## This part defines parameters for the Invenio OAI gateway.
## Useful if you are running Invenio as OAI data provider.
## CFG_OAI_ID_FIELD -- OAI identifier MARC field:
CFG_OAI_ID_FIELD = 909COo
## CFG_OAI_SET_FIELD -- OAI set MARC field:
CFG_OAI_SET_FIELD = 909COp
## CFG_OAI_DELETED_POLICY -- OAI deletedrecordspolicy
## (no/transient/persistent).
CFG_OAI_DELETED_POLICY = no
## CFG_OAI_ID_PREFIX -- OAI identifier prefix:
CFG_OAI_ID_PREFIX = atlantis.cern.ch
## CFG_OAI_SAMPLE_IDENTIFIER -- OAI sample identifier:
CFG_OAI_SAMPLE_IDENTIFIER = oai:atlantis.cern.ch:CERN-TH-4036
## CFG_OAI_IDENTIFY_DESCRIPTION -- description for the OAI Identify verb:
CFG_OAI_IDENTIFY_DESCRIPTION = oaiatlantis.cern.ch:oai:atlantis.cern.ch:CERN-TH-4036http://atlantis.cern.ch/Free and unlimited use by anybody with obligation to refer to original recordFull content, i.e. preprints may not be harvested by robotsSubmission restricted. Submitted documents are subject of approval by OAI repository admins.
## CFG_OAI_LOAD -- OAI number of records in a response:
CFG_OAI_LOAD = 1000
## CFG_OAI_EXPIRE -- OAI resumptionToken expiration time:
CFG_OAI_EXPIRE = 90000
## CFG_OAI_SLEEP -- service unavailable between two consecutive
## requests for CFG_OAI_SLEEP seconds:
CFG_OAI_SLEEP = 10
##################################
## Part 5: WebSubmit parameters ##
##################################
## This section contains some configuration parameters for WebSubmit
## module. Please note that WebSubmit is mostly configured on
## run-time via its WebSubmit Admin web interface. The parameters
## below are the ones that you do not probably want to modify during
## the runtime.
## CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT -- the fulltext
## documents are stored under "/opt/invenio/var/data/files/gX/Y"
## directories where X is 0,1,... and Y stands for bibdoc ID. Thusly
## documents Y are grouped into directories X and this variable
## indicates the maximum number of documents Y stored in each
## directory X. This limit is imposed solely for filesystem
## performance reasons in order not to have too many subdirectories in
## a given directory.
CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT = 5000
## CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS -- a comma-separated
## list of document extensions not listed in Python standard mimetype
## library that should be recognized by Invenio.
CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS = hpg,link,lis,llb,mat,mpp,msg,docx,docm,xlsx,xlsm,xlsb,pptx,pptm,ppsx,ppsm
## CFG_BIBDOCFILE_USE_XSENDFILE -- if your web server supports
## XSendfile header, you may want to enable this feature in order for
## to Invenio tell the web server to stream files for download (after
## proper authorization checks) by web server's means. This helps to
## liberate Invenio worker processes from being busy with sending big
## files to clients. The web server will take care of that. Note:
## this feature is still somewhat experimental. Note: when enabled
## (set to 1), then you have to also regenerate Apache vhost conf
## snippets (inveniocfg --update-config-py --create-apache-conf).
CFG_BIBDOCFILE_USE_XSENDFILE = 0
## CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY -- a number between 0 and
## 1 that indicates probability with which MD5 checksum will be
## verified when streaming bibdocfile-managed files. (0.1 will cause
## the check to be performed once for every 10 downloads)
CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY = 0.1
## CFG_OPENOFFICE_SERVER_HOST -- the host where an OpenOffice Server is
## listening to. If localhost an OpenOffice server will be started
## automatically if it is not already running.
## Note: if you set this to an empty value this will disable the usage of
## OpenOffice for converting documents.
## If you set this to something different than localhost you'll have to take
## care to have an OpenOffice server running on the corresponding host and
## to install the same OpenOffice release both on the client and on the server
## side.
## In order to launch an OpenOffice server on a remote machine, just start
## the usual 'soffice' executable in this way:
## $> soffice -headless -nologo -nodefault -norestore -nofirststartwizard \
## .. -accept=socket,host=HOST,port=PORT;urp;StarOffice.ComponentContext
CFG_OPENOFFICE_SERVER_HOST = localhost
## CFG_OPENOFFICE_SERVER_PORT -- the port where an OpenOffice Server is
## listening to.
CFG_OPENOFFICE_SERVER_PORT = 2002
## CFG_OPENOFFICE_USER -- the user that will be used to launch the OpenOffice
## client. It is recommended to set this to a user who don't own files, like
## e.g. 'nobody'. You should also authorize your Apache server user to be
## able to become this user, e.g. by adding to your /etc/sudoers the following
## line:
## "apache ALL=(nobody) NOPASSWD: ALL"
## provided that apache is the username corresponding to the Apache user.
## On some machine this might be apache2 or www-data.
CFG_OPENOFFICE_USER = nobody
#################################
## Part 6: BibIndex parameters ##
#################################
## This section contains some configuration parameters for BibIndex
## module. Please note that BibIndex is mostly configured on run-time
## via its BibIndex Admin web interface. The parameters below are the
## ones that you do not probably want to modify very often during the
## runtime.
## CFG_BIBINDEX_FULLTEXT_INDEX_LOCAL_FILES_ONLY -- when fulltext indexing, do
## you want to index locally stored files only, or also external URLs?
## Use "0" to say "no" and "1" to say "yes".
CFG_BIBINDEX_FULLTEXT_INDEX_LOCAL_FILES_ONLY = 0
## CFG_BIBINDEX_REMOVE_STOPWORDS -- when indexing, do we want to remove
## stopwords? Use "0" to say "no" and "1" to say "yes".
CFG_BIBINDEX_REMOVE_STOPWORDS = 0
## CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS -- characters considered as
## alphanumeric separators of word-blocks inside words. You probably
## don't want to change this.
CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS = \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
## CFG_BIBINDEX_CHARS_PUNCTUATION -- characters considered as punctuation
## between word-blocks inside words. You probably don't want to
## change this.
CFG_BIBINDEX_CHARS_PUNCTUATION = \.\,\:\;\?\!\"
## CFG_BIBINDEX_REMOVE_HTML_MARKUP -- should we attempt to remove HTML markup
## before indexing? Use 1 if you have HTML markup inside metadata
## (e.g. in abstracts), use 0 otherwise.
CFG_BIBINDEX_REMOVE_HTML_MARKUP = 0
## CFG_BIBINDEX_REMOVE_LATEX_MARKUP -- should we attempt to remove LATEX markup
## before indexing? Use 1 if you have LATEX markup inside metadata
## (e.g. in abstracts), use 0 otherwise.
CFG_BIBINDEX_REMOVE_LATEX_MARKUP = 0
## CFG_BIBINDEX_MIN_WORD_LENGTH -- minimum word length allowed to be added to
## index. The terms smaller then this amount will be discarded.
## Useful to keep the database clean, however you can safely leave
## this value on 0 for up to 1,000,000 documents.
CFG_BIBINDEX_MIN_WORD_LENGTH = 0
## CFG_BIBINDEX_URLOPENER_USERNAME and CFG_BIBINDEX_URLOPENER_PASSWORD --
## access credentials to access restricted URLs, interesting only if
## you are fulltext-indexing files located on a remote server that is
## only available via username/password. But it's probably better to
## handle this case via IP or some convention; the current scheme is
## mostly there for demo only.
CFG_BIBINDEX_URLOPENER_USERNAME = mysuperuser
CFG_BIBINDEX_URLOPENER_PASSWORD = mysuperpass
## CFG_INTBITSET_ENABLE_SANITY_CHECKS --
## Enable sanity checks for integers passed to the intbitset data
## structures. It is good to enable this during debugging
## and to disable this value for speed improvements.
CFG_INTBITSET_ENABLE_SANITY_CHECKS = False
## CFG_BIBINDEX_PERFORM_OCR_ON_DOCNAMES -- regular expression that matches
## docnames for which OCR is desired (set this to .* in order to enable
## OCR in general, set this to empty in order to disable it.)
CFG_BIBINDEX_PERFORM_OCR_ON_DOCNAMES = scan-.*
## CFG_BIBINDEX_SPLASH_PAGES -- regular expression that matches URLs
## that are not to be indexed but that indirectly refers to documents
## that are supposed to be indexed.
CFG_BIBINDEX_SPLASH_PAGES = http://documents\.cern\.ch/setlink\?.*
## CFG_BIBINDEX_AUTHOR_WORD_INDEX_EXCLUDE_FIRST_NAMES -- do we want
## the author word index to exclude first names to keep only last
## names? If set to True, then for the author `Bernard, Denis', only
## `Bernard' will be indexed in the word index, not `Denis'. Note
## that if you change this variable, you have to re-index the author
## index via `bibindex -w author -R'.
CFG_BIBINDEX_AUTHOR_WORD_INDEX_EXCLUDE_FIRST_NAMES = False
#######################################
## Part 7: Access control parameters ##
#######################################
## This section contains some configuration parameters for the access
## control system. Please note that WebAccess is mostly configured on
## run-time via its WebAccess Admin web interface. The parameters
## below are the ones that you do not probably want to modify very
## often during the runtime. (If you do want to modify them during
## runtime, for example te deny access temporarily because of backups,
## you can edit access_control_config.py directly, no need to get back
## here and no need to redo the make process.)
## CFG_ACCESS_CONTROL_LEVEL_SITE -- defines how open this site is.
## Use 0 for normal operation of the site, 1 for read-only site (all
## write operations temporarily closed), 2 for site fully closed,
## 3 for also disabling any database connection.
## Useful for site maintenance.
CFG_ACCESS_CONTROL_LEVEL_SITE = 0
## CFG_ACCESS_CONTROL_LEVEL_GUESTS -- guest users access policy. Use
## 0 to allow guest users, 1 not to allow them (all users must login).
CFG_ACCESS_CONTROL_LEVEL_GUESTS = 0
## CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS -- account registration and
## activation policy. When 0, users can register and accounts are
## automatically activated. When 1, users can register but admin must
## activate the accounts. When 2, users cannot register nor update
## their email address, only admin can register accounts. When 3,
## users cannot register nor update email address nor password, only
## admin can register accounts. When 4, the same as 3 applies, nor
## user cannot change his login method. When 5, then the same as 4
## applies, plus info about how to get an account is hidden from the
## login page.
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS = 0
## CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN -- limit account
## registration to certain email addresses? If wanted, give domain
## name below, e.g. "cern.ch". If not wanted, leave it empty.
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN =
## CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS -- send a
## notification email to the administrator when a new account is
## created? Use 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS = 0
## CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT -- send a
## notification email to the user when a new account is created in order to
## to verify the validity of the provided email address? Use
## 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT = 1
## CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION -- send a
## notification email to the user when a new account is activated?
## Use 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION = 0
## CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION -- send a
## notification email to the user when a new account is deleted or
## account demand rejected? Use 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION = 0
## CFG_APACHE_PASSWORD_FILE -- the file where Apache user credentials
## are stored. Must be an absolute pathname. If the value does not
## start by a slash, it is considered to be the filename of a file
## located under prefix/var/tmp directory. This is useful for the
## demo site testing purposes. For the production site, if you plan
## to restrict access to some collections based on the Apache user
## authentication mechanism, you should put here an absolute path to
## your Apache password file.
CFG_APACHE_PASSWORD_FILE = demo-site-apache-user-passwords
## CFG_APACHE_GROUP_FILE -- the file where Apache user groups are
## defined. See the documentation of the preceding config variable.
CFG_APACHE_GROUP_FILE = demo-site-apache-user-groups
###################################
## Part 8: WebSession parameters ##
###################################
## This section contains some configuration parameters for tweaking
## session handling.
## CFG_WEBSESSION_EXPIRY_LIMIT_DEFAULT -- number of days after which a session
## and the corresponding cookie is considered expired.
CFG_WEBSESSION_EXPIRY_LIMIT_DEFAULT = 2
## CFG_WEBSESSION_EXPIRY_LIMIT_REMEMBER -- number of days after which a session
## and the corresponding cookie is considered expired, when the user has
## requested to permanently stay logged in.
CFG_WEBSESSION_EXPIRY_LIMIT_REMEMBER = 365
## CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS -- when user requested
## a password reset, for how many days is the URL valid?
CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS = 3
## CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS -- when an account
## activation email was sent, for how many days is the URL valid?
CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS = 3
## CFG_WEBSESSION_NOT_CONFIRMED_EMAIL_ADDRESS_EXPIRE_IN_DAYS -- when
## user won't confirm his email address and not complete
## registeration, after how many days will it expire?
CFG_WEBSESSION_NOT_CONFIRMED_EMAIL_ADDRESS_EXPIRE_IN_DAYS = 10
## CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS -- when set to 1, the session
## system allocates the same uid=0 to all guests users regardless of where they
## come from. 0 allocate a unique uid to each guest.
CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS = 0
################################
## Part 9: BibRank parameters ##
################################
## This section contains some configuration parameters for the ranking
## system.
## CFG_BIBRANK_SHOW_READING_STATS -- do we want to show reading
## similarity stats? ('People who viewed this page also viewed')
CFG_BIBRANK_SHOW_READING_STATS = 1
## CFG_BIBRANK_SHOW_DOWNLOAD_STATS -- do we want to show the download
## similarity stats? ('People who downloaded this document also
## downloaded')
CFG_BIBRANK_SHOW_DOWNLOAD_STATS = 1
## CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS -- do we want to show download
## history graph?
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS = 1
## CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS_CLIENT_IP_DISTRIBUTION -- do we
## want to show a graph representing the distribution of client IPs
## downloading given document?
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS_CLIENT_IP_DISTRIBUTION = 0
## CFG_BIBRANK_SHOW_CITATION_LINKS -- do we want to show the 'Cited
## by' links? (useful only when you have citations in the metadata)
CFG_BIBRANK_SHOW_CITATION_LINKS = 1
## CFG_BIBRANK_SHOW_CITATION_STATS -- de we want to show citation
## stats? ('Cited by M recors', 'Co-cited with N records')
CFG_BIBRANK_SHOW_CITATION_STATS = 1
## CFG_BIBRANK_SHOW_CITATION_GRAPHS -- do we want to show citation
## history graph?
CFG_BIBRANK_SHOW_CITATION_GRAPHS = 1
####################################
## Part 10: WebComment parameters ##
####################################
## This section contains some configuration parameters for the
## commenting and reviewing facilities.
## CFG_WEBCOMMENT_ALLOW_COMMENTS -- do we want to allow users write
## public comments on records?
CFG_WEBCOMMENT_ALLOW_COMMENTS = 1
## CFG_WEBCOMMENT_ALLOW_REVIEWS -- do we want to allow users write
## public reviews of records?
CFG_WEBCOMMENT_ALLOW_REVIEWS = 1
## CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS -- do we want to allow short
## reviews, that is just the attribution of stars without submitting
## detailed review text?
CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS = 0
## CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN -- if users
## report a comment to be abusive, how many they have to be before the
## site admin is alerted?
CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN = 5
## CFG_WEBCOMMENT_NB_COMMENTS_IN_DETAILED_VIEW -- how many comments do
## we display in the detailed record page upon welcome?
CFG_WEBCOMMENT_NB_COMMENTS_IN_DETAILED_VIEW = 1
## CFG_WEBCOMMENT_NB_REVIEWS_IN_DETAILED_VIEW -- how many reviews do
## we display in the detailed record page upon welcome?
CFG_WEBCOMMENT_NB_REVIEWS_IN_DETAILED_VIEW = 1
## CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL -- do we notify the site
## admin after every comment?
CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL = 1
## CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS -- how many
## elapsed seconds do we consider enough when checking for possible
## multiple comment submissions by a user?
CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS = 20
## CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS -- how many
## elapsed seconds do we consider enough when checking for possible
## multiple review submissions by a user?
CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS = 20
## CFG_WEBCOMMENT_USE_RICH_EDITOR -- enable the WYSIWYG
## Javascript-based editor when user edits comments?
CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR = False
## CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL -- the email address from which the
## alert emails will appear to be sent:
CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL = info@invenio-software.org
## CFG_WEBCOMMENT_DEFAULT_MODERATOR -- if no rules are
## specified to indicate who is the comment moderator of
## a collection, this person will be used as default
CFG_WEBCOMMENT_DEFAULT_MODERATOR = info@invenio-software.org
## CFG_WEBCOMMENT_USE_JSMATH_IN_COMMENTS -- do we want to allow the use
## of jsmath plugin to render latex input in comments?
CFG_WEBCOMMENT_USE_JSMATH_IN_COMMENTS = 1
## CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION -- allow comment author to
## delete its own comment?
CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION = 1
##################################
## Part 11: BibSched parameters ##
##################################
## This section contains some configuration parameters for the
## bibliographic task scheduler.
## CFG_BIBSCHED_REFRESHTIME -- how often do we want to refresh
## bibsched monitor? (in seconds)
CFG_BIBSCHED_REFRESHTIME = 5
## CFG_BIBSCHED_LOG_PAGER -- what pager to use to view bibsched task
## logs?
CFG_BIBSCHED_LOG_PAGER = /bin/more
## CFG_BIBSCHED_GC_TASKS_OLDER_THAN -- after how many days to perform the
## gargbage collector of BibSched queue (i.e. removing/moving task to archive).
CFG_BIBSCHED_GC_TASKS_OLDER_THAN = 30
## CFG_BIBSCHED_GC_TASKS_TO_REMOVE -- list of BibTask that can be safely
## removed from the BibSched queue once they are DONE.
CFG_BIBSCHED_GC_TASKS_TO_REMOVE = bibindex,bibreformat,webcoll,bibrank,inveniogc
## CFG_BIBSCHED_GC_TASKS_TO_ARCHIVE -- list of BibTasks that should be safely
## archived out of the BibSched queue once they are DONE.
CFG_BIBSCHED_GC_TASKS_TO_ARCHIVE = bibupload,oaiarchive
## CFG_BIBSCHED_MAX_NUMBER_CONCURRENT_TASKS -- maximum number of BibTasks
## that can run concurrently.
## NOTE: concurrent tasks are still considered as an experimental
## feature. Please keep this value set to 1 on production environments.
CFG_BIBSCHED_MAX_NUMBER_CONCURRENT_TASKS = 1
## CFG_BIBSCHED_PROCESS_USER -- bibsched and bibtask processes must
## usually run under the same identity as the Apache web server
## process in order to share proper file read/write privileges. If
## you want to force some other bibsched/bibtask user, e.g. because
## you are using a local `invenio' user that belongs to your
## `www-data' Apache user group and so shares writing rights with your
## Apache web server process in this way, then please set its username
## identity here. Otherwise we shall check whether your
## bibsched/bibtask processes are run under the same identity as your
## Apache web server process (in which case you can leave the default
## empty value here).
CFG_BIBSCHED_PROCESS_USER =
###################################
## Part 12: WebBasket parameters ##
###################################
## CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS -- a safety limit for
## a maximum number of displayed baskets
CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS = 20
## CFG_WEBBASKET_USE_RICH_TEXT_EDITOR -- enable the WYSIWYG
## Javascript-based editor when user edits comments in WebBasket?
CFG_WEBBASKET_USE_RICH_TEXT_EDITOR = False
##################################
## Part 13: WebAlert parameters ##
##################################
## This section contains some configuration parameters for the
## automatic email notification alert system.
## CFG_WEBALERT_ALERT_ENGINE_EMAIL -- the email address from which the
## alert emails will appear to be sent:
CFG_WEBALERT_ALERT_ENGINE_EMAIL = info@invenio-software.org
## CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL -- how many records
## at most do we send in an outgoing alert email?
CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL = 20
## CFG_WEBALERT_MAX_NUM_OF_CHARS_PER_LINE_IN_ALERT_EMAIL -- number of
## chars per line in an outgoing alert email?
CFG_WEBALERT_MAX_NUM_OF_CHARS_PER_LINE_IN_ALERT_EMAIL = 72
## CFG_WEBALERT_SEND_EMAIL_NUMBER_OF_TRIES -- when sending alert
## emails fails, how many times we retry?
CFG_WEBALERT_SEND_EMAIL_NUMBER_OF_TRIES = 3
## CFG_WEBALERT_SEND_EMAIL_SLEEPTIME_BETWEEN_TRIES -- when sending
## alert emails fails, what is the sleeptime between tries? (in
## seconds)
CFG_WEBALERT_SEND_EMAIL_SLEEPTIME_BETWEEN_TRIES = 300
####################################
## Part 14: WebMessage parameters ##
####################################
## CFG_WEBMESSAGE_MAX_SIZE_OF_MESSAGE -- how large web messages do we
## allow?
CFG_WEBMESSAGE_MAX_SIZE_OF_MESSAGE = 20000
## CFG_WEBMESSAGE_MAX_NB_OF_MESSAGES -- how many messages for a
## regular user do we allow in its inbox?
CFG_WEBMESSAGE_MAX_NB_OF_MESSAGES = 30
## CFG_WEBMESSAGE_DAYS_BEFORE_DELETE_ORPHANS -- how many days before
## we delete orphaned messages?
CFG_WEBMESSAGE_DAYS_BEFORE_DELETE_ORPHANS = 60
##################################
## Part 15: MiscUtil parameters ##
##################################
## CFG_MISCUTIL_SQL_USE_SQLALCHEMY -- whether to use SQLAlchemy.pool
## in the DB engine of Invenio. It is okay to enable this flag
## even if you have not installed SQLAlchemy. Note that Invenio will
## loose some perfomance if this option is enabled.
CFG_MISCUTIL_SQL_USE_SQLALCHEMY = False
## CFG_MISCUTIL_SQL_RUN_SQL_MANY_LIMIT -- how many queries can we run
## inside run_sql_many() in one SQL statement? The limit value
## depends on MySQL's max_allowed_packet configuration.
CFG_MISCUTIL_SQL_RUN_SQL_MANY_LIMIT = 10000
## CFG_MISCUTIL_SMTP_HOST -- which server to use as outgoing mail server to
## send outgoing emails generated by the system, for example concerning
## submissions or email notification alerts.
CFG_MISCUTIL_SMTP_HOST = localhost
## CFG_MISCUTIL_SMTP_PORT -- which port to use on the outgoing mail server
## defined in the previous step.
CFG_MISCUTIL_SMTP_PORT = 25
## CFG_MISCUTILS_DEFAULT_PROCESS_TIMEOUT -- the default number of seconds after
## which a process launched trough shellutils.run_process_with_timeout will
## be killed. This is useful to catch runaway processes.
CFG_MISCUTIL_DEFAULT_PROCESS_TIMEOUT = 300
## CFG_PLOTEXTRACTOR_ARXIV_BASE -- for acquiring source tarballs for plot
## extraction, where should we look? If nothing is set, we'll just go
## to arXiv, but this can be a filesystem location, too
CFG_PLOTEXTRACTOR_ARXIV_BASE =
#################################
## Part 16: BibEdit parameters ##
#################################
## CFG_BIBEDIT_TIMEOUT -- when a user edits a record, this record is
## locked to prevent other users to edit it at the same time.
## How many seconds of inactivity before the locked record again will be free
## for other people to edit?
CFG_BIBEDIT_TIMEOUT = 3600
## CFG_BIBEDIT_LOCKLEVEL -- when a user tries to edit a record which there
## is a pending bibupload task for in the queue, this shouldn't be permitted.
## The lock level determines how thouroughly the queue should be investigated
## to determine if this is the case.
## Level 0 - always permits editing, doesn't look at the queue
## (unsafe, use only if you know what you are doing)
## Level 1 - permits editing if there are no queued bibedit tasks for this record
## (safe with respect to bibedit, but not for other bibupload maintenance jobs)
## Level 2 - permits editing if there are no queued bibupload tasks of any sort
## (safe, but may lock more than necessary if many cataloguers around)
## Level 3 - permits editing if no queued bibupload task concerns given record
## (safe, most precise locking, but slow,
## checks for 001/EXTERNAL_SYSNO_TAG/EXTERNAL_OAIID_TAG)
## The recommended level is 3 (default) or 2 (if you use maintenance jobs often).
CFG_BIBEDIT_LOCKLEVEL = 3
## CFG_BIBEDIT_PROTECTED_FIELDS -- a comma-separated list of fields that BibEdit
## will not allow to be added, edited or deleted. Wildcards are not supported,
## but conceptually a wildcard is added at the end of every field specification.
## Examples:
## 500A - protect all MARC fields with tag 500 and first indicator A
## 5 - protect all MARC fields in the 500-series.
## 909C_a - protect subfield a in tag 909 with first indicator C and empty
## second indicator
## Note that 001 is protected by default, but if protection of other
## identifiers or automated fields is a requirement, they should be added to
## this list.
CFG_BIBEDIT_PROTECTED_FIELDS =
## CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING -- maximum number of records
## that can be modified instantly using the multi-record editor. Above
## this limit, modifications will only be executed in limited hours.
CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING = 2000
## CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING -- maximum number of records
## that can be send for modification without having a superadmin role.
## If the number of records is between CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING
## and this number, the modifications will take place only in limited hours.
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING = 20000
## CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING_TIME -- Allowed time to
## execute modifications on records, when the number exceeds
## CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING.
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING_TIME = 22:00-05:00
###################################
## Part 17: BibUpload parameters ##
###################################
## CFG_BIBUPLOAD_REFERENCE_TAG -- where do we store references?
CFG_BIBUPLOAD_REFERENCE_TAG = 999
## CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG -- where do we store external
## system numbers? Useful for matching when our records come from an
## external digital library system.
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG = 970__a
## CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG -- where do we store OAI ID tags
## of harvested records? Useful for matching when we harvest stuff
## via OAI that we do not want to reexport via Invenio OAI; so records
## may have only the source OAI ID stored in this tag (kind of like
## external system number too).
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG = 035__a
## CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG -- where do we store OAI SRC
## tags of harvested records? Useful for matching when we harvest stuff
## via OAI that we do not want to reexport via Invenio OAI; so records
## may have only the source OAI SRC stored in this tag (kind of like
## external system number too). Note that the field should be the same of
## CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG.
CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG = 035__9
## CFG_BIBUPLOAD_STRONG_TAGS -- a comma-separated list of tags that
## are strong enough to resist the replace mode. Useful for tags that
## might be created from an external non-metadata-like source,
## e.g. the information about the number of copies left.
CFG_BIBUPLOAD_STRONG_TAGS = 964
## CFG_BIBUPLOAD_CONTROLLED_PROVENANCE_TAGS -- a comma-separated list
## of tags that contain provenance information that should be checked
## in the bibupload correct mode via matching provenance codes. (Only
## field instances of the same provenance information would be acted
## upon.) Please specify the whole tag info up to subfield codes.
CFG_BIBUPLOAD_CONTROLLED_PROVENANCE_TAGS = 6531_9
## CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS -- a comma-separated list of system
## paths from which it is allowed to take fulltextes that will be uploaded via
## FFT (CFG_TMPDIR is included by default).
CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS = /tmp,/home
## CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE -- do we want to serialize
## internal representation of records (Pythonic record structure) into
## the database? This can improve internal processing speed of some
## operations at the price of somewhat bigger disk space usage.
## If you change this value after some records have already been added
## to your installation, you may want to run:
## $ /opt/invenio/bin/inveniocfg --reset-recstruct-cache
## in order to either erase the cache thus freeing database space,
## or to fill the cache for all records that have not been cached yet.
CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE = 1
## CFG_BATCHUPLOADER_FILENAME_MATCHING_POLICY -- a comma-separated list
## indicating which fields match the file names of the documents to be
## uploaded.
## The matching will be done in the same order as the list provided.
CFG_BATCHUPLOADER_FILENAME_MATCHING_POLICY = reportnumber,recid
## CFG_BATCHUPLOADER_DAEMON_DIR -- Directory where the batchuploader daemon
## will look for the subfolders metadata and document by default.
## If path is relative, CFG_PREFIX will be joined as a prefix
CFG_BATCHUPLOADER_DAEMON_DIR = var/batchupload
## CFG_BATCHUPLOADER_WEB_ROBOT_AGENT -- Comma-separated list to specify the
## agents permitted when calling batch uploader web interface
## cdsweb.cern.ch/batchuploader/robotupload
## if using a curl, eg: curl xxx -A invenio_webupload
CFG_BATCHUPLOADER_WEB_ROBOT_AGENT = invenio_webupload
## CFG_BATCHUPLOADER_WEB_ROBOT_RIGHTS -- Access list specifying for each
## IP address, which collections are allowed using batch uploader robot
## interface.
CFG_BATCHUPLOADER_WEB_ROBOT_RIGHTS = {
'10.0.0.1': ['BOOK', 'REPORT'], # Example 1
'10.0.0.2': ['POETRY', 'PREPRINT'], # Example 2
}
####################################
## Part 18: BibCatalog parameters ##
####################################
## EXPERIMENTAL: Please do not use.
CFG_BIBCATALOG_SYSTEM =
CFG_BIBCATALOG_SYSTEM_RT_CLI = /usr/bin/rt
CFG_BIBCATALOG_SYSTEM_RT_URL = http://localhost/rt3
CFG_BIBCATALOG_QUEUES = General
####################################
## Part 19: BibFormat parameters ##
####################################
## CFG_BIBFORMAT_HIDDEN_TAGS -- comma-separated list of MARC tags that
## are not shown to users not having cataloging authorizations.
CFG_BIBFORMAT_HIDDEN_TAGS = 595
##########################
## THAT's ALL, FOLKS! ##
##########################
diff --git a/modules/bibcirculation/lib/bibcirculation_webinterface.py b/modules/bibcirculation/lib/bibcirculation_webinterface.py
index 8084c6ad6..7252a47ca 100644
--- a/modules/bibcirculation/lib/bibcirculation_webinterface.py
+++ b/modules/bibcirculation/lib/bibcirculation_webinterface.py
@@ -1,747 +1,747 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Bibcirculation web interface """
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
# others invenio imports
from invenio.config import CFG_SITE_LANG, \
CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS
from invenio.webuser import getUid, page_not_authorized, isGuestUser, \
collect_user_info
from invenio.webpage import page, pageheaderonly, pagefooteronly
from invenio.search_engine import create_navtrail_links, \
guess_primary_collection_of_a_record, \
get_colID, check_user_can_view_record, \
record_exists
from invenio.urlutils import redirect_to_url, \
make_canonical_urlargd
from invenio.messages import gettext_set_language
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
# bibcirculation imports
bibcirculation_templates = invenio.template.load('bibcirculation')
from invenio.bibcirculation import perform_new_request, \
perform_new_request_send, \
perform_get_holdings_information, \
perform_borrower_loans, \
perform_loanshistoricaloverview, \
display_ill_form, \
ill_register_request, \
ill_request_with_recid, \
ill_register_request_with_recid
class WebInterfaceYourLoansPages(WebInterfaceDirectory):
"""Defines the set of /yourloans pages."""
_exports = ['', 'display', 'loanshistoricaloverview']
def index(self, req, form):
""" The function called by default
"""
redirect_to_url(req, "%s/yourloans/display?%s" % (CFG_SITE_URL,
req.args))
def display(self, req, form):
"""
Displays all loans of a given user
@param ln: language
@return the page for inbox
"""
argd = wash_urlargd(form, {'barcode': (str, ""),
'borrower_id': (int, 0),
'request_id': (int, 0)})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourloans/display" % \
(CFG_SITE_URL,),
navmenuid="yourloans")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourloans/display%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use loans."))
body = perform_borrower_loans(uid=uid,
barcode=argd['barcode'],
borrower_id=argd['borrower_id'],
request_id=argd['request_id'],
ln=argd['ln'])
return page(title = _("Your Loans"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "yourloans")
def loanshistoricaloverview(self, req, form):
"""
Show loans historical overview.
"""
argd = wash_urlargd(form, {})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourloans/loanshistoricaloverview" % \
(CFG_SITE_URL,),
navmenuid="yourloans")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourloans/loanshistoricaloverview%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use loans."))
body = perform_loanshistoricaloverview(uid=uid,
ln=argd['ln'])
return page(title = _("Loans - historical overview"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "yourloans")
class WebInterfaceILLPages(WebInterfaceDirectory):
"""Defines the set of /ill pages."""
_exports = ['', 'display', 'register_request']
def index(self, req, form):
""" The function called by default
"""
redirect_to_url(req, "%s/ill/display?%s" % (CFG_SITE_URL,
req.args))
def display(self, req, form):
"""
Displays all loans of a given user
@param ln: language
@return the page for inbox
"""
argd = wash_urlargd(form, {})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/ill/display" % \
(CFG_SITE_URL,),
navmenuid="ill")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/ill/display%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use ill."))
body = display_ill_form(ln=argd['ln'])
return page(title = _("Interlibrary loan request for books"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "ill")
def register_request(self, req, form):
"""
Displays all loans of a given user
@param ln: language
@return the page for inbox
"""
argd = wash_urlargd(form, {'ln': (str, ""),
'title': (str, ""),
'authors': (str, ""),
'place': (str, ""),
'publisher': (str, ""),
'year': (str, ""),
'edition': (str, ""),
'isbn': (str, ""),
'period_of_interest_from': (str, ""),
'period_of_interest_to': (str, ""),
'additional_comments': (str, ""),
'conditions': (str, ""),
'only_edition': (str, ""),
})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/ill/register_request" % \
(CFG_SITE_URL,),
navmenuid="ill")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/ill/register_request%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use ill."))
body = ill_register_request(uid=uid,
title=argd['title'],
authors=argd['authors'],
place=argd['place'],
publisher=argd['publisher'],
year=argd['year'],
edition=argd['edition'],
isbn=argd['isbn'],
period_of_interest_from = argd['period_of_interest_from'],
period_of_interest_to = argd['period_of_interest_to'],
additional_comments = argd['additional_comments'],
conditions = argd['conditions'],
only_edition = argd['only_edition'],
request_type='book',
ln=argd['ln'])
return page(title = _("Interlibrary loan request for books"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "ill")
class WebInterfaceHoldingsPages(WebInterfaceDirectory):
"""Defines the set of /holdings pages."""
_exports = ['', 'display', 'request', 'send', 'ill_request_with_recid', 'ill_register_request_with_recid']
def __init__(self, recid=-1):
self.recid = recid
def index(self, req, form):
"""
Redirects to display function
"""
return self.display(req, form)
def display(self, req, form):
"""
Show the tab 'holdings'.
"""
argd = wash_urlargd(form, {'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'voted': (int, -1),
'reported': (int, -1),
})
_ = gettext_set_language(argd['ln'])
record_exists_p = record_exists(self.recid)
if record_exists_p != 1:
if record_exists_p == -1:
msg = _("The record has been deleted.")
else:
msg = _("Requested record does not seem to exist.")
msg = '' + msg + ''
title, description, keywords = \
websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])
return page(title = title,
show_title_p = False,
body = msg,
description = description,
keywords = keywords,
uid = getUid(req),
language = argd['ln'],
req = req,
navmenuid='search')
body = perform_get_holdings_information(self.recid, req, argd['ln'])
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
'%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
navtrail += ' > '% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += ''
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
metaheaderadd = "" % CFG_SITE_URL,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
# Return the same page wether we ask for /record/123 or /record/123/
__call__ = index
def request(self, req, form):
"""
Show new hold request form.
"""
argd = wash_urlargd(form, {'ln': (str, ""), 'barcode': (str, "")})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
body = perform_new_request(recid=self.recid,
barcode=argd['barcode'],
ln=argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../holdings/request",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/record/%s/holdings/request%s" % (
CFG_SITE_URL,
self.recid,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
'%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
navtrail += ' > '% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += ''
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
metaheaderadd = "" % CFG_SITE_URL,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
def send(self, req, form):
"""
Create a new hold request.
"""
argd = wash_urlargd(form, {'period_from': (str, ""),
'period_to': (str, ""),
'barcode': (str, "")
})
uid = getUid(req)
body = perform_new_request_send(recid=self.recid,
uid=uid,
period_from=argd['period_from'],
period_to=argd['period_to'],
barcode=argd['barcode'])
ln = CFG_SITE_LANG
_ = gettext_set_language(ln)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=ln)
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
tabs = [(unordered_tabs[tab_id]['label'], \
'%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
navtrail += ' > '% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += ''
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__,
language=argd['ln'], req=req)
def ill_request_with_recid(self, req, form):
"""
Show ILL request form.
"""
argd = wash_urlargd(form, {'ln': (str, "")})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
body = ill_request_with_recid(recid=self.recid,
ln=argd['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../holdings/ill_request_with_recid",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/record/%s/holdings/ill_request_with_recid%s" % (
CFG_SITE_URL,
self.recid,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
'%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
navtrail += ' > '% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += ''
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
metaheaderadd = "" % CFG_SITE_URL,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
def ill_register_request_with_recid(self, req, form):
"""
Register ILL request.
"""
argd = wash_urlargd(form, {'ln': (str, ""),
'period_of_interest_from': (str, ""),
'period_of_interest_to': (str, ""),
'additional_comments': (str, ""),
'conditions': (str, ""),
'only_edition': (str, ""),
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
body = ill_register_request_with_recid(recid=self.recid,
uid=uid,
period_of_interest_from = argd['period_of_interest_from'],
period_of_interest_to = argd['period_of_interest_to'],
additional_comments = argd['additional_comments'],
conditions = argd['conditions'],
only_edition = argd['only_edition'],
ln=argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../holdings/ill_request_with_recid",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/record/%s/holdings/ill_request_with_recid%s" % (
CFG_SITE_URL,
self.recid,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
'%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
navtrail += ' > '% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += ''
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
diff --git a/modules/bibformat/doc/hacking/bibformat-api.webdoc b/modules/bibformat/doc/hacking/bibformat-api.webdoc
index 91fe267fe..bbfd497fe 100644
--- a/modules/bibformat/doc/hacking/bibformat-api.webdoc
+++ b/modules/bibformat/doc/hacking/bibformat-api.webdoc
@@ -1,891 +1,889 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
****************************************************************************
** IMPORTANT NOTE: Note that this documentation is an updated version of **
** an earlier technical draft of BibFormat specifications. Please first **
** refer to the BibFormat admin guide. **
****************************************************************************
Technical Overview of the new BibFormat
=======================================
Contents:
1. Python API
2. The philosophy behind BibFormat
3. Differences between the old PHP version and the new Pythonic version
4. Migrating from the previous PHP BibFormat version to the new Pythonic version
5. Specifications of the new BibFormat configuration files.
1. Python API
The APIs of bibformat.py consists in these functions:
def format_record(recID, of, ln=CFG_SITE_LANG, verbose=0,
search_pattern=None, xml_record=None, user_info=None,
on_the_fly=False):
"""
Formats a record given its ID (or its XML representation)
and an output format.
Returns a formatted version of the record in the specified
language, with pattern context, and specified output format.
The function will define by itself which format template must be
applied.
Parameters that allow contextual formatting (like 'search_pattern'
and 'user_info') are useful only when doing on-the-fly
formatting, or when caching with care (e.g. caching all formatted
versions of a record for each possible 'ln').
The arguments are as follows:
recID - the ID of the record to format. If ID does not exist
the function returns empty string or an error
string, depending on level of verbosity.
If 'xml_record' parameter is specified, 'recID'
is ignored
of - an output format code. If 'of' does not exist as code in
output format, the function returns empty
string or an error string, depending on level
of verbosity. ;of' is case insensitive.
ln - the language to use to format the record. If
'ln' is an unknown language, or translation
does not exist, default CFG_SITE_LANG language
will be applied whenever possible.
Allows contextual formatting.
verbose - the level of verbosity in case of errors/warnings
0 - Silent mode
5 - Prints only errors
9 - Prints errors and warnings
search_pattern - the pattern used as search query when asked to
format this record (User request in web
interface). Allows contextual formatting.
xml_record - an XML string representation of the record to
format. If it is specified, recID parameter is
ignored. The XML must be pasable by BibRecord.
user_info - allows to grant access to some functionalities
on a page depending on the user's
priviledges. 'user_info' is the same structure
as the one returned by webuser.collect_user_info(req),
(that is a dictionary).
on_the_fly - if False, try to return an already preformatted
version of the record in the database.
"""
Example:
>> from invenio.bibformat import format_record
>> format_record(5, "hb", "fr")
def format_records(recIDs, of, ln=CFG_SITE_LANG, verbose=0, search_pattern=None,
xml_records=None, user_info=None, record_prefix=None,
record_separator=None, record_suffix=None,
prologue="", epilogue="", req=None, on_the_fly=False):
"""
Returns a list of formatted records given by a list of record IDs or a
list of records as xml.
Adds a prefix before each record, a suffix after each record,
plus a separator between records.
Also add optional prologue and epilogue to the complete formatted list.
You can either specify a list of record IDs to format, or a list of
xml records, but not both (if both are specified recIDs is ignored).
'record_separator' is a function that returns a string as separator between
records. The function must take an integer as unique parameter,
which is the index in recIDs (or xml_records) of the record that has
just been formatted. For example separator(i) must return the separator
between recID[i] and recID[i+1]. Alternatively separator can be a single
string, which will be used to separate all formatted records.
The same applies to 'record_prefix' and 'record_suffix'.
'req' is an optional parameter on which the result of the function
are printed lively (prints records after records) if it is given.
Note that you should set 'req' content-type by yourself, and send
http header before calling this function as it will not do it.
This function takes the same parameters as 'format_record' except for:
recIDs - a list of record IDs to format
xml_records - a list of xml string representions of the records to
format. If this list is specified, 'recIDs' is ignored.
record_prefix - a string or a function the takes the index of the record
in 'recIDs' or 'xml_records' for which the function must
return a string.
Printed before each formatted record.
record_separator - either a string or a function that returns string to
separate formatted records. The function takes the index
of the record in 'recIDs' or 'xml_records' that is being
formatted.
record_prefix - a string or a function the takes the index of the record
in 'recIDs' or 'xml_records' for which the function must
return a string.
Printed after each formatted record
req - an optional request object on which formatted records
can be printed (for "live" output )
prologue - a string printed before all formatted records string
epilogue - a string printed after all formatted records string
on_the_fly - if False, try to return an already preformatted version
of the records in the database
"""
def get_output_format_content_type(of):
"""
Returns the content type (eg. 'text/html' or 'application/ms-excel') \
of the given output format.
The function takes this mandatory parameter:
of - the code of output format for which we want to get the content type
"""
def record_get_xml(recID, format='xm', decompress=zlib.decompress):
"""
Returns an XML string of the record given by recID.
The function builds the XML directly from the database,
without using the standard formatting process.
'format' allows to define the flavour of XML:
- 'xm' for standard XML
- 'marcxml' for MARC XML
- 'oai_dc' for OAI Dublin Core
- 'xd' for XML Dublin Core
If record does not exist, returns empty string.
The function takes the following parameters:
recID - the id of the record to retrieve
format - the XML flavor in which we want to get the record
decompress _ a function used to decompress the record from the database
"""
The API of the BibFormat Object ('bfo') given as a parameter to
format function of format elements consist in the following
functions. This API is to be used only inside format elements.
def control_field(self, tag, escape='0'):
"""
Returns the value of control field given by tag in record.
If the value does not exist, returns empty string
The returned value is always a string.
'escape' parameter allows to escape special characters
of the field. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - remove unsafe HTML tags (Eg. keep <br />)
3 - Mix of mode 1 and 2. If value of field starts with
<!-- HTML -->, then use mode 2. Else use mode 1.
4 - Remove all HTML tags
5 - Same as 2, with more tags allowed (like <img>)
6 - Same as 3, with more tags allowed (like <img>)
The arguments are:
tag - the marc code of a field
escape - 1 if returned value should be escaped. Else 0.
(see above for other modes)
"""
def field(self, tag, escape='0'):
"""
Returns the value of the field corresponding to tag in the
current record.
If the value does not exist, returns empty string
Else returns the same as bfo.fields(..)[0] (see docstring below).
'escape' parameter allows to escape special characters
of the field. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - remove unsafe HTML tags (Eg. keep <br />)
3 - Mix of mode 1 and 2. If value of field starts with
<!-- HTML -->, then use mode 2. Else use mode 1.
4 - Remove all HTML tags
5 - Same as 2, with more tags allowed (like <img>)
6 - Same as 3, with more tags allowed (like <img>)
The arguments are:
tag - the marc code of a field
escape - 1 if returned value should be escaped. Else 0.
(see above for other modes)
"""
def fields(self, tag, escape='0', repeatable_subfields_p=False):
"""
Returns the list of values corresonding to "tag".
If tag has an undefined subcode (such as 999C5),
the function returns a list of dictionaries, whoose keys
are the subcodes and the values are the values of tag.subcode.
If the tag has a subcode, simply returns list of values
corresponding to tag.
Eg. for given MARC:
999C5 $a value_1a $b value_1b
999C5 $b value_2b
999C5 $b value_3b $b value_3b_bis
>> bfo.fields('999C5b')
>> ['value_1b', 'value_2b', 'value_3b', 'value_3b_bis']
>> bfo.fields('999C5')
>> [{'a':'value_1a', 'b':'value_1b'},
{'b':'value_2b'},
{'b':'value_3b'}]
By default the function returns only one value for each
subfield (that is it considers that repeatable subfields are
not allowed). It is why in the above example 'value3b_bis' is
not shown for bfo.fields('999C5'). (Note that it is not
defined which of value_3b or value_3b_bis is returned). This
is to simplify the use of the function, as most of the time
subfields are not repeatable (in that way we get a string
instead of a list). You can allow repeatable subfields by
setting 'repeatable_subfields_p' parameter to True. In
this mode, the above example would return:
>> bfo.fields('999C5b', repeatable_subfields_p=True)
>> ['value_1b', 'value_2b', 'value_3b']
>> bfo.fields('999C5', repeatable_subfields_p=True)
>> [{'a':['value_1a'], 'b':['value_1b']},
{'b':['value_2b']},
{'b':['value_3b', 'value3b_bis']}]
NOTICE THAT THE RETURNED STRUCTURE IS DIFFERENT. Also note
that whatever the value of 'repeatable_subfields_p' is,
bfo.fields('999C5b') always show all fields, even repeatable
ones. This is because the parameter has no impact on the
returned structure (it is always a list).
'escape' parameter allows to escape special characters
of the fields. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - remove unsafe HTML tags (Eg. keep <br />)
3 - Mix of mode 1 and 2. If value of field starts with
<!-- HTML -->, then use mode 2. Else use mode 1.
4 - Remove all HTML tags
5 - Same as 2, with more tags allowed (like <img>)
6 - Same as 3, with more tags allowed (like <img>)
The arguments are:
tag - the marc code of a field
escape - 1 if returned value should be escaped. Else 0.
(see above for other modes)
repeatable_subfields_p - if True, returns the list of
subfields in the dictionary @return
values of field tag in record """
def kb(self, kb, string, default=""):
"""
Returns the value of the "string" in the knowledge base "kb".
If kb does not exist or string does not exist in kb,
returns 'default' string or empty string if not specified
The arguments are as follows:
kb - the knowledge base name in which we want to find the mapping.
If it does not exist the function returns the original
'string' parameter value. The name is case insensitive (Uses
the SQL 'LIKE' syntax to retrieve value).
string - the value for which we want to find a translation-
If it does not exist the function returns 'default' string.
The string is case insensitive (Uses the SQL 'LIKE' syntax
to retrieve value).
default - a default value returned if 'string' not found in 'kb'.
"""
def get_record(self):
"""
Returns the record encapsulated in bfo as a BibRecord structure.
You can get full access to the record through bibrecord.py functions.
"""
Example (from inside BibFormat element):
>> bfo.field("520.a")
>> 'We present a quantitative appraisal of the physics potential
for neutrino experiments.'
>>
>> bfo.control_field("001")
>> '12'
>>
>> bfo.fields("700.a")
>>['Alekhin, S I', 'Anselmino, M', 'Ball, R D', 'Boglione, M']
>>
>> bfo.kb("DBCOLLID2COLL", "ARTICLE")
>> 'Published Article'
>>
>> bfo.kb("DBCOLLID2COLL", "not in kb", "My Value")
>> 'My Value'
Moreover you can have access to the language requested for the
formatting, the search pattern used by the user in the web
interface and the userID by directly getting the attribute from 'bfo':
bfo.lang
"""
Returns the language that was asked to be used for the
formatting. Always returns a string.
"""
bfo.search_pattern
"""
Returns the search pattern specified by the user when
the record had to be formatted. Always returns a string.
"""
bfo.user_info
"""
Returns a dictionary with information about current user.
The returned dictionary has the following structure:
user_info = {
'remote_ip' : '',
'remote_host' : '',
'referer' : '',
'uri' : '',
'agent' : '',
- 'apache_user' : '',
- 'apache_group' : [],
'uid' : -1,
'nickname' : '',
'email' : '',
'group' : [],
'guest' : '1'
}
"""
bfo.uid
"""
! DEPRECATED: use bfo.user_info['uid'] instead
"""
bfo.recID
"""
Returns the id of the record
"""
bfo.req
"""
! DEPRECATED: use bfo.user_info instead
"""
bfo.format
"""
! DEPRECATED: use bfo.output_format instead
"""
bfo.output_format
"""
Returns the format in which the record is being formatted
"""
Example (from inside BibFormat element):
>> bfo.lang
>> 'en'
>>
>> bfo.search_pattern
>> 'mangano and neutrino and factory'
2. The philosophy behind BibFormat
BibFormat is in charge of formatting the bibliographic records that
are displayed to your users. As you potentially have a huge amount of
bibliographic records, you cannot specify manually for each of them
how it should be formatted. This is why you can define rules that will
allow BibFormat to understand which kind of formatting to apply to a given
record. You define this set of rules in what is called an "output
format".
You can have different output formats, each with its own characteristics.
For example you certainly want that when multiple bibliographic records are
displayed at the same time (as it happens in search results), only
short versions are shown to the user, while a detailed record is
preferable when a single record is displayed. You might also want to
let your users decide which kind of output they want. For example you
might need to display HTML for regular web browsing, but would also
give a BibTeX version of the bibliographic reference for direct
inclusion in a LaTeX document.
See section 5.1 to learn how to create or modify output formats.
While output formats define what kind of formatting must be applied,
they do not define HOW the formatting is done. This is the role of the
"format templates", which define the layout and look of a
bibliographic reference. These format templates are rather easy to
write if you know a little bit of HTML (see section 5.2 "Format
templates specifications"). You will certainly have to create
different format templates, for different kinds of records. For
example you might want records that contain pictures to display them,
maybe with captions, while records that do not have pictures limited
to printing a title and an abstract.
In summary, you have different output formats (like 'brief HTML',
'detailed HTML' or 'BibTeX') that call different format templates
according to some criteria.
There is still one kind of configuration file that we have not talked
about: the "format elements". These are the "bricks" that you use in
format templates, to get the values of a record. You will learn to use
them in your format template in section 5.2 "Format templates
specifications", but you will mostly not need to modify them or create
new ones. However if you do need to edit one, read section 5.3 "Format
elements specifications" (And if you know Python it will be easy, as
they are written in Python).
Finally BibFormat can make use of mapping tables called "knowledge
bases". Their primary use is to act like a translation table, to
normalize records before displaying them. For example, you can say
that records that have value "Phys Rev D" or "Physical Review D" for
field "published in" must display "Phys Rev : D." to users. See
section 5.4 to learn how to edit knowledge bases.
In summary, there are three layers. Output formats:
+-----------------------------------------------------+
| Output Format | (Layer 1)
| eg: HTML_Brief.bfo |
+-----------------------------------------------------+
call one of several `format templates':
+-------------------------+ +-------------------------+
| Format Template | | Format Template | (Layer 2)
| eg: preprint.bft | | eg: default.bft |
+-------------------------+ +-------------------------+
that use one or several format elements:
+--------------+ +----------------+ +-----------------+
|Format Element| |Format Element | | Format Element | (Layer 3)
|eg: authors.py| |eg: abstract.py | | eg: title.py |
+--------------+ +----------------+ +-----------------+
3. Differences between the old PHP version and the new Pythonic version
The most noticeable differences are:
a) "Behaviours" have been renamed "Output formats".
b) "Formats" have been renamed "Format templates". They are now
written in HTML.
c) "User defined functions" have been dropped.
d) "Extraction rules" have been dropped.
e) "Link rules" have been dropped.
f) "File formats" have been dropped.
g) "Format elements" have been introduced. They are written in Python,
and can simulate c), d) and e).
h) Formats can be managed through web interface or through
human-readable config files.
i) Introduction of tools like validator and dependencies checker.
j) Better support for multi-language formatting.
Some of the advantages are:
+ Management of formats is much clearer and easier (less concepts,
more tools).
+ Writing formats is easier to learn : less concepts
to learn, redesigned work-flow, use of existing well known and
well documented languages.
+ Editing formats is easier: You can use your preferred HTML editor such as
Emacs, Dreamweaver or Frontpage to modify templates, or any text
editor for output formats and format elements. You can also use the
simplified web administration interface.
+ Faster and more powerful templating system.
+ Separation of business logic (output formats, format elements)
and presentation layer (format templates). This makes the management
of formats simpler.
The disadvantages are:
- No backward compatibility with old formats.
- Stricter separation of business logic and presentation layer:
no more use of statements such as if(), forall() inside templates,
and this requires more work to put logic inside format elements.
4. Migrating from the previous PHP BibFormat version to the new Pythonic version
Old BibFormat formats are no longer compatible with the new BibFormat
files. If you have not modified the "Formats" or modified only a
little bit the "Behaviours", then the transition will be painless and
automatic. Otherwise you will have to manually rewrite some of the
formats. This should however not be a big problem. Firstly because the
Invenio installation will provide both versions of BibFormat for
some time. Secondly because both BibFormat versions can run side by
side, so that you can migrate your formats while your server still
works with the old formats. Thirdly because we provide a migration
kit that can help you go through this process. Finally because the
migration is not so difficult, and because it will be much easier for
you to customize how BibFormat formats your bibliographic data.
Concerning the migration kit it can:
a) Effortlessly migrate your behaviours, unless they include complex
logic, which usually they don't.
b) Help you migrate formats to format templates and format elements.
c) Effortlessly migrate your knowledge bases.
Point b) is the most difficult to achieve: previous formats did mix
business logic and code for the presentation, and could use PHP
functions. The new BibFormat separates business logic and
presentation, and does not support PHP. The transition kit will try to
move business logic to the format elements, and the presentation to
the format templates. These files will be created for you, includes
the original code and, if possible, a proposal of Python
translation. We recommend that you do not to use the transition kit to
translate formats, especially if you have not modified default
formats, or only modified default formats in some limited places. You
will get cleaner code if you write format elements and format
templates yourself.
5. Specifications of the new BibFormat configuration files.
BibFormat uses human readable configuration files. However (apart
from format elements) these files can be edited and managed through
a web interface.
5.1 Output formats specifications
Output formats specify rules that define which format template
to use to format a record.
While the syntax of output formats is basic, we recommend that you use
the web interface do edit them, to be sure that you make no error.
The syntax of output format is the following one. First you
define which field code you put as the conditon for the rule.
You suffix it with a column. Then on next lines, define the values of
the condition, followed by --- and then the filename of the template
to use:
tag 980.a:
PICTURE --- PICTURE_HTML_BRIEF.bft
PREPRINT --- PREPRINT_HTML_BRIEF.bft
PUBLICATION --- PUBLICATION_HTML_BRIEF.bft
This means that if value of field 980.a is equal to PICTURE, then we
will use format template PICTURE_HTML_BRIEF.bft. Note that you must
use the filename of the template, not the name. Also note that spaces
at the end or beginning are not considered. On the following lines,
you can either put other conditions on tag 980.a, or add another tag on
which you want to put conditions.
At the end you can add a default condition:
default: PREPRINT_HTML_BRIEF.bft
which means that if no condition is matched, a format suitable for
Preprints will be used to format the current record.
The output format file could then look like this:
tag 980.a:
PICTURE --- PICTURE_HTML_BRIEF.bft
PREPRINT --- PREPRINT_HTML_BRIEF.bft
PUBLICATION --- PUBLICATION_HTML_BRIEF.bft
tag 8560.f:
.*@cern.ch --- SPECIAL_MEMBER_FORMATTING.bft
default: PREPRINT_HTML_BRIEF.bft
You can add as many rules as you want. Keep in mind that they are read
in the order they are defined, and that only first rule that
matches will be used.
Notice the condition on tag 8560.f: it uses a regular expression to
match any email address that ends with @cern.ch (the regular
expression must be understandable by Python)
Some other considerations on the management of output formats:
- Format outputs must be placed inside directory
/etc/bibformat/outputs/ of your Invenio installation.
- Note that as long as you have not provided a name to an output
THROUGH the web interface, it will not be available as a choice
for your users in some parts of Invenio.
- You should remove output formats THROUGH the web interface.
- The format extension of output format is .bfo
5.2 Format templates specifications
Format templates are written in HTML-like syntax. You can use the
standard HTML and CSS markup languague to do the formatting. The best
thing to do is to create a page in your favourite editor, and once you
are glad with it, add the dynamic part of the page, that is print the
fields of the records. Let's say you have defined this page:
<h1>Some title</h1>
<p><i>Abstract: </i>Some abstract</p>
Then you want that instead of "Some title" and "Some abstract", the
value of the current record that is being displayed is used. To do so,
you must use a format element brick. Either you know the name of the
brick by heart, or you look for it in the elements documentation (see
section 5.3). For example you would find there that you can print the
title of the record by writting the HTML tag <BFE_TITLE /> in your
format template, with parameter 'default' for a default value.
<h1><BFE_TITLE default="No Title"/></h1>
<p><BFE_ABSTRACT limit="1" prefix="<i>Abstract: </i>"
default="No abstract"/></p>
Notice that <BFE_ABSTRACT /> has a parameter "limit" that <BFE_title/>
had not ("limit" allows to limit the number of sentences of the
abstract, according to the documentation). Note that while format
elements might have different parameters, they always can take the the
three following ones: "prefix" and "suffix", whose values are printed
only if the element is not empty, and "default", which is printed only
if element is an empty string. We have used "prefix" for the abstract,
so that the label "<i>Abstract: </i>" is only printed if the record
has an abstract.
You should also provide these tags in all of your templates:
-<name>a name for this template in the admin web interface</name>
-<description>a description to be used in admin web interface for
this template</description>
Another feature of the templates is the support for multi-languages
outputs. You can include <lang> tags, which contain tags labeled with
the names of the languages supported in Invenio. For example, one
might write:
<lang><en>A record:</en><fr>Une notice:</fr></lang>
<h1><BFE_TITLE default="No Title"/></h1>
<p><BFE_ABSTRACT limit="1" prefix="<i>Abstract: </i>"
default="No abstract"/></p>
When doing this you should at least make sure that the default
language of your server installation is available in each <lang>
tag. It is the one that is used if the requested language to display
the record is not available. Note that we could also provide a
translation in a similar way for the "No Title" default value inside
<BFE_Title /> tag.
Some other considerations on the use of elements inside templates:
-Format elements names are not case sensitive
-Format elements names always start with <BFE_
-Format elements parameters can contain '<' characters,
and quotes different from the kind that delimit parameters (you can
for example have <BFE_Title default='<a href="#">No Title</a>'/> )
-Format templates must be placed inside the directory
/etc/bibformat/templates/ of your Invenio installation
-The format extension of a template is .bft
Trick: you can use the <BFE_FIELD tag="245__a" /> to print the value
of any field 245 $a in your templates. This practice is however not
recommended because it would necessitate to revise all format
templates if you change meaning of the MARC code schema.
5.3 Format elements specifications
Format elements are the bricks used in format templates to provide the
dynamic contents inside format templates.
For the most basic format elements, you do not even need to write
them: as long as you define `tag names' for MARC tags in the BibIndex
Admin's Manage logical fields interface (database table tag),
BibFormat knows which field must be printed when <BFE_tag_name/> is
used inside a template.
However for more complex processing, you will need to write a format
element. A format element is written in Python. Therefore its file
extension is ".py". The name you choose for the file is the one that
will be used inside format template to call the element, so choose it
carefully such that it is not too long, but self explanatory (you can
prefix the filename with BFE or not, but the element will always be
called with prefix <BFE_ inside templates). Then you just need to
drop the file in the lib/python/invenio/bibformat_elements/ directory
of your Invenio installation. Inside your file you have to define
a function named "format_element", which takes at least a "bfo"
parameter (bfo for BibFormat Object). The function must return a
string:
def format_element(bfo):
out = ""
return out
You can have as many parameters as you want, as long as you make sure
that parameter bfo is here. Let's see how to define an element that
will print a brief title. It will take a parameter 'limit' that will
limit the number of characters printed. We can provide some
documentation for the elemen in the docstring of the
function.
def format_element(bfo, limit="10"):
"""
Prints a short title
@param limit a limit for the number of printed characters
"""
out = ""
return out
Note that we put a default value of 10 in the 'limit' parameter. To
get some value of a field, we must request the 'bfo' object. For
example we can get the value of field 245.a (field "title"):
def format_element(bfo, limit="10"):
"""
Prints a short title
@param limit a limit for the number of printed characters
"""
title = bfo.field('245.a')
limit = int(limit)
if limit > len(title):
limit = len(title)
return title[:limit]
As format elements are written in Python, we have decided not to give
permission to edit elements through the web interface. Firstly for
security reasons. Secondly because Python requires correct indentation,
which is difficult to achieve through a web interface.
You can have access to the documentation of your element through a web
interface. This is very useful when you are writing a format template,
to see which elements are available, what they do, which parameters they
take, what are the default values of parameters, etc. The
documentation is automatically extracted from format elements.
Here follows an sample documentation generated for the element
<BFE_TITLE />:
+--------------------------------------------------------------------------------------------+
| TITLE |
| ----- |
| <BFE_TITLE separator="..." prefix="..." suffix="..." default="..." /> |
| |
| Prints the title of a record. |
| |
| Parameters: |
| separator - separator between the different titles. |
| prefix - A prefix printed only if the record has a value for this element. |
| suffix - A suffix printed only if the record has a value for this element. |
| default - A default value printed if the record has no value for this element. |
| |
| See also: |
| Format templates that use this element |
| The Python code of this element |
+--------------------------------------------------------------------------------------------+
The more you provide documentation in the docstring of your elements,
the easier it will be to write format template afterwards.
Some remarks concerning format elements:
-parameters are always string values
-if no value is given as parameter in format the template, then the
value of parameter is "" (emtpy string)
-the docstring should contain a description, followed by
"@param parameter: some description for parameter" for each
parameter (to give description for each parameter
in element documentation), and @see an_element.py, another_element.py
(to link to other elements in the documentation). Similar to JavaDoc.
-the following names cannot be used as parameters:
"default", "prefix", "suffix" and escape. They can however always be
used in the format template for any element.
Another important remark concerns the 'escaping' of output of format
elements. In most cases, format elements output is to be used for
HTML/XML. Therefore special characters such as < or & have to be
'escaped', replaced by '<' and '&'. This is why all outputs
produced by format elements are automatically escaped by BibFormat,
unless specified otherwise. This means that you do not have to care
about meta-data that would break your HTML displaying or XML export
such as a physics formula like 'a < b'. Please also note that value
given in 'prefix', 'suffix' and 'default' parameters are not escaped,
such that you can safely use HTML tags for these.
There are always cases where the default 'escaping' behaviour of
BibFormat is not desired. For example when you explicitely output HTML
text, like links: you do not want to see them escaped. The first way
to avoid this is to modify the call to your format element in the
format template, by setting the default 'escape' parameter to 0:
This is however inconvenient as you have to possibly need to modify a
lot of templates. The other way of doing is to add another function to
your format element, named 'escape':
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
In that way all calls to your format element will produce unescaped
output. You will have to take care of escaping values "manually" in
your format element code, in order to avoid non valid outputs or XSS
vulnerabilities. There are methods to ease the escaping in your code
described in section 1.
Please also note that if you use this method, your element can still
be escaped if a call to your element from a format template
explicitely specifies to escape value using parameter 'escape'.
5.4 Knowledge bases specifications
Knowledge bases cannot be managed through configuration files.
You can very easily add new bases and mappings using the given web GUI.
-- End of file --
diff --git a/modules/bibformat/lib/bibformat_engine.py b/modules/bibformat/lib/bibformat_engine.py
index 9fdc63828..843da3d2e 100644
--- a/modules/bibformat/lib/bibformat_engine.py
+++ b/modules/bibformat/lib/bibformat_engine.py
@@ -1,2057 +1,2055 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Formats a single XML Marc record using specified format.
There is no API for the engine. Instead use bibformat.py.
SEE: bibformat.py, bibformat_utils.py
"""
__revision__ = "$Id$"
import re
import sys
import os
import inspect
import traceback
import zlib
import cgi
from invenio.config import \
CFG_PATH_PHP, \
CFG_BINDIR, \
CFG_SITE_LANG
from invenio.errorlib import \
register_errors, \
get_msgs_for_code_list
from invenio.bibrecord import \
create_record, \
record_get_field_instances, \
record_get_field_value, \
record_get_field_values
from invenio.bibformat_xslt_engine import format
from invenio.dbquery import run_sql
from invenio.messages import \
language_list_long, \
wash_language, \
gettext_set_language
from invenio import bibformat_dblayer
from invenio.bibformat_config import \
CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION, \
CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION, \
CFG_BIBFORMAT_TEMPLATES_PATH, \
CFG_BIBFORMAT_ELEMENTS_PATH, \
CFG_BIBFORMAT_OUTPUTS_PATH, \
CFG_BIBFORMAT_ELEMENTS_IMPORT_PATH
from invenio.bibformat_utils import \
record_get_xml, \
parse_tag
from invenio.htmlutils import \
HTMLWasher, \
cfg_html_buffer_allowed_tag_whitelist, \
cfg_html_buffer_allowed_attribute_whitelist
from invenio.webuser import collect_user_info
from invenio.bibknowledge import get_kbr_values
from HTMLParser import HTMLParseError
if CFG_PATH_PHP: #Remove when call_old_bibformat is removed
from xml.dom import minidom
import tempfile
# Cache for data we have already read and parsed
format_templates_cache = {}
format_elements_cache = {}
format_outputs_cache = {}
html_field = '' # String indicating that field should be
# treated as HTML (and therefore no escaping of
# HTML tags should occur.
# Appears in some field values.
washer = HTMLWasher() # Used to remove dangerous tags from HTML
# sources
# Regular expression for finding ... tag in format templates
pattern_lang = re.compile(r'''
#closing start tag
(?P.*?) #anything but the next group (greedy)
() #end tag
''', re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Builds regular expression for finding each known language in tags
ln_pattern_text = r"<("
for lang in language_list_long(enabled_langs_only=False):
ln_pattern_text += lang[0] +r"|"
ln_pattern_text = ln_pattern_text.rstrip(r"|")
ln_pattern_text += r")>(.*?)\1>"
ln_pattern = re.compile(ln_pattern_text, re.IGNORECASE | re.DOTALL)
# Regular expression for finding text to be translated
translation_pattern = re.compile(r'_\((?P.*?)\)_', \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding tag in format templates
pattern_format_template_name = re.compile(r'''
#closing start tag
(?P.*?) #name value. any char that is not end tag
()(\n)? #end tag
''', re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding tag in format templates
pattern_format_template_desc = re.compile(r'''
#closing start tag
(?P.*?) #description value. any char that is not end tag
(\n)? #end tag
''', re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding tags in format templates
pattern_tag = re.compile(r'''
[^/\s]+) #any char but a space or slash
\s* #any number of spaces
(?P(\s* #params here
(?P([^=\s])*)\s* #param name: any chars that is not a white space or equality. Followed by space(s)
=\s* #equality: = followed by any number of spaces
(?P[\'"]) #one of the separators
(?P.*?) #param value: any chars that is not a separator like previous one
(?P=sep) #same separator as starting one
)*) #many params
\s* #any number of spaces
(/)?> #end of the tag
''', re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding params inside tags in format templates
pattern_function_params = re.compile('''
(?P([^=\s])*)\s* # Param name: any chars that is not a white space or equality. Followed by space(s)
=\s* # Equality: = followed by any number of spaces
(?P[\'"]) # One of the separators
(?P.*?) # Param value: any chars that is not a separator like previous one
(?P=sep) # Same separator as starting one
''', re.VERBOSE | re.DOTALL )
# Regular expression for finding format elements "params" attributes
# (defined by @param)
pattern_format_element_params = re.compile('''
@param\s* # Begins with AT param keyword followed by space(s)
(?P[^\s=]*):\s* # A single keyword and comma, then space(s)
#(=\s*(?P[\'"]) # Equality, space(s) and then one of the separators
#(?P.*?) # Default value: any chars that is not a separator like previous one
#(?P=sep) # Same separator as starting one
#)?\s* # Default value for param is optional. Followed by space(s)
(?P.*) # Any text that is not end of line (thanks to MULTILINE parameter)
''', re.VERBOSE | re.MULTILINE)
# Regular expression for finding format elements "see also" attribute
# (defined by @see)
pattern_format_element_seealso = re.compile('''@see:\s*(?P.*)''',
re.VERBOSE | re.MULTILINE)
#Regular expression for finding 2 expressions in quotes, separated by
#comma (as in template("1st","2nd") )
#Used when parsing output formats
## pattern_parse_tuple_in_quotes = re.compile('''
## (?P[\'"])
## (?P.*)
## (?P=sep1)
## \s*,\s*
## (?P[\'"])
## (?P.*)
## (?P=sep2)
## ''', re.VERBOSE | re.MULTILINE)
def call_old_bibformat(recID, of="HD", on_the_fly=False, verbose=0):
"""
FIXME: REMOVE FUNCTION WHEN MIGRATION IS DONE
Calls BibFormat for the record RECID in the desired output format 'of'.
@param recID: record ID to format
@param of: output format to be used for formatting
@param on_the_fly: if False, try to return an already preformatted version of the record in the database
@param verbose: verbosity
Note: this functions always try to return HTML, so when
bibformat returns XML with embedded HTML format inside the tag
FMT $g, as is suitable for prestoring output formats, we
perform un-XML-izing here in order to return HTML body only.
"""
out = ""
res = []
if not on_the_fly:
# look for formatted record existence:
query = "SELECT value, last_updated FROM bibfmt WHERE "\
"id_bibrec='%s' AND format='%s'" % (recID, of)
res = run_sql(query, None, 1)
if res:
# record 'recID' is formatted in 'of', so print it
if verbose == 9:
last_updated = res[0][1]
out += """\n
Found preformatted output for record %i (cache updated on %s).
""" % (recID, last_updated)
decompress = zlib.decompress
return "%s" % decompress(res[0][0])
else:
# record 'recID' is not formatted in 'of',
# so try to call BibFormat on the fly or use default format:
if verbose == 9:
out += """\n
Formatting record %i on-the-fly with old BibFormat.
""" % recID
# Retrieve MARCXML
# Build it on-the-fly only if 'call_old_bibformat' was called
# with format=xm and on_the_fly=True
xm_record = record_get_xml(recID, 'xm',
on_the_fly=(on_the_fly and of == 'xm'))
## import platform
## # Some problem have been found using either popen() or os.system().
## # Here is a temporary workaround until the issue is solved.
## if platform.python_compiler().find('Red Hat') > -1:
## # use os.system
(result_code, result_path) = tempfile.mkstemp()
command = "( %s/bibformat otype=%s ) > %s" % \
(CFG_BINDIR, of, result_path)
(xm_code, xm_path) = tempfile.mkstemp()
xm_file = open(xm_path, "w")
xm_file.write(xm_record)
xm_file.close()
command = command + " <" + xm_path
os.system(command)
result_file = open(result_path,"r")
bibformat_output = result_file.read()
result_file.close()
os.close(result_code)
os.remove(result_path)
os.close(xm_code)
os.remove(xm_path)
## else:
## # use popen
## pipe_input, pipe_output, pipe_error = os.popen3(["%s/bibformat" % CFG_BINDIR,
## "otype=%s" % format],
## 'rw')
## pipe_input.write(xm_record)
## pipe_input.flush()
## pipe_input.close()
## bibformat_output = pipe_output.read()
## pipe_output.close()
## pipe_error.close()
if bibformat_output.startswith(""):
dom = minidom.parseString(bibformat_output)
for e in dom.getElementsByTagName('subfield'):
if e.getAttribute('code') == 'g':
for t in e.childNodes:
out += t.data.encode('utf-8')
else:
out += bibformat_output
return out
def format_record(recID, of, ln=CFG_SITE_LANG, verbose=0,
search_pattern=None, xml_record=None, user_info=None):
"""
Formats a record given output format. Main entry function of
bibformat engine.
Returns a formatted version of the record in the specified
language, search pattern, and with the specified output format.
The function will define which format template must be applied.
You can either specify an record ID to format, or give its xml
representation. if 'xml_record' is not None, then use it instead
of recID.
'user_info' allows to grant access to some functionalities on a
page depending on the user's priviledges. 'user_info' is the same
object as the one returned by 'webuser.collect_user_info(req)'
@param recID: the ID of record to format
@param of: an output format code (or short identifier for the output format)
@param ln: the language to use to format the record
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings, stop if error in format elements
9: errors and warnings, stop if error (debug mode ))
@param search_pattern: list of strings representing the user request in web interface
@param xml_record: an xml string representing the record to format
@param user_info: the information of the user who will view the formatted page
@return: formatted record
"""
if search_pattern is None:
search_pattern = []
out = ""
errors_ = []
# Temporary workflow (during migration of formats):
# Call new BibFormat
# But if format not found for new BibFormat, then call old BibFormat
#Create a BibFormat Object to pass that contain record and context
bfo = BibFormatObject(recID, ln, search_pattern, xml_record, user_info, of)
if of.lower() != 'xm' and \
(not bfo.get_record() or len(bfo.get_record()) <= 1):
# Record only has recid: do not format, excepted
# for xm format
return ""
#Find out which format template to use based on record and output format.
template = decide_format_template(bfo, of)
if verbose == 9 and template is not None:
out += """\n
Using %s template for record %i.
""" % (template, recID)
############### FIXME: REMOVE WHEN MIGRATION IS DONE ###############
path = "%s%s%s" % (CFG_BIBFORMAT_TEMPLATES_PATH, os.sep, template)
if template is None or not os.access(path, os.R_OK):
# template not found in new BibFormat. Call old one
if verbose == 9:
if template is None:
out += """\n
No template found for output format %s and record %i.
(Check invenio.err log file for more details)
""" % (of, recID)
else:
out += """\n
Template %s could not be read.
""" % (template)
if CFG_PATH_PHP:
if verbose == 9:
out += """\n
Using old BibFormat for record %s.
""" % recID
return out + call_old_bibformat(recID, of=of, on_the_fly=True,
verbose=verbose)
############################# END ##################################
error = get_msgs_for_code_list([("ERR_BIBFORMAT_NO_TEMPLATE_FOUND", of)],
stream='error', ln=CFG_SITE_LANG)
errors_.append(error)
if verbose == 0:
register_errors(error, 'error')
elif verbose > 5:
return out + error[0][1]
return out
# Format with template
(out_, errors) = format_with_format_template(template, bfo, verbose)
errors_.extend(errors)
out += out_
return out
def decide_format_template(bfo, of):
"""
Returns the format template name that should be used for formatting
given output format and BibFormatObject.
Look at of rules, and take the first matching one.
If no rule matches, returns None
To match we ignore lettercase and spaces before and after value of
rule and value of record
@param bfo: a BibFormatObject
@param of: the code of the output format to use
"""
output_format = get_output_format(of)
for rule in output_format['rules']:
if rule['field'].startswith('00'):
# Rule uses controlfield
value = bfo.control_field(rule['field']).strip() #Remove spaces
else:
# Rule uses datafield
value = bfo.field(rule['field']).strip() #Remove spaces
pattern = rule['value'].strip() #Remove spaces
match_obj = re.match(pattern, value, re.IGNORECASE)
if match_obj is not None and \
match_obj.start() == 0 and match_obj.end() == len(value):
return rule['template']
template = output_format['default']
if template != '':
return template
else:
return None
def format_with_format_template(format_template_filename, bfo,
verbose=0, format_template_code=None):
""" Format a record given a
format template. Also returns errors
Returns a formatted version of the record represented by bfo,
in the language specified in bfo, and with the specified format template.
If format_template_code is provided, the template will not be loaded from
format_template_filename (but format_template_filename will still be used to
determine if bft or xsl transformation applies). This allows to preview format
code without having to save file on disk.
@param format_template_filename: the dilename of a format template
@param bfo: the object containing parameters for the current formatting
@param format_template_code: if not empty, use code as template instead of reading format_template_filename (used for previews)
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings,
9: errors and warnings, stop if error (debug mode ))
@return: tuple (formatted text, errors)
"""
_ = gettext_set_language(bfo.lang)
def translate(match):
"""
Translate matching values
"""
word = match.group("word")
translated_word = _(word)
return translated_word
errors_ = []
if format_template_code is not None:
format_content = str(format_template_code)
else:
format_content = get_format_template(format_template_filename)['code']
if format_template_filename is None or \
format_template_filename.endswith("."+CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION):
# .bft
filtered_format = filter_languages(format_content, bfo.lang)
localized_format = translation_pattern.sub(translate, filtered_format)
(evaluated_format, errors) = eval_format_template_elements(localized_format,
bfo,
verbose)
errors_ = errors
else:
#.xsl
# Fetch MARCXML. On-the-fly xm if we are now formatting in xm
xml_record = '\n' + \
record_get_xml(bfo.recID, 'xm', on_the_fly=False)
# Transform MARCXML using stylesheet
evaluated_format = format(xml_record, template_source=format_content)
return (evaluated_format, errors_)
def eval_format_template_elements(format_template, bfo, verbose=0):
"""
Evalutes the format elements of the given template and replace each element with its value.
Also returns errors.
Prepare the format template content so that we can directly replace the marc code by their value.
This implies: 1) Look for special tags
2) replace special tags by their evaluation
@param format_template: the format template code
@param bfo: the object containing parameters for the current formatting
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings,
9: errors and warnings, stop if error (debug mode ))
@return: tuple (result, errors)
"""
errors_ = []
# First define insert_element_code(match), used in re.sub() function
def insert_element_code(match):
"""
Analyses 'match', interpret the corresponding code, and return the result of the evaluation.
Called by substitution in 'eval_format_template_elements(...)'
@param match: a match object corresponding to the special tag that must be interpreted
"""
function_name = match.group("function_name")
try:
format_element = get_format_element(function_name, verbose)
except Exception, e:
if verbose >= 5:
return '' + \
cgi.escape(str(e)).replace('\n', ' ') + \
''
if format_element is None:
error = get_msgs_for_code_list([("ERR_BIBFORMAT_CANNOT_RESOLVE_ELEMENT_NAME", function_name)],
stream='error', ln=CFG_SITE_LANG)
errors_.append(error)
if verbose >= 5:
return '' + \
error[0][1]+''
else:
params = {}
# Look for function parameters given in format template code
all_params = match.group('params')
if all_params is not None:
function_params_iterator = pattern_function_params.finditer(all_params)
for param_match in function_params_iterator:
name = param_match.group('param')
value = param_match.group('value')
params[name] = value
# Evaluate element with params and return (Do not return errors)
(result, errors) = eval_format_element(format_element,
bfo,
params,
verbose)
errors_.append(errors)
return result
# Substitute special tags in the format by our own text.
# Special tags have the form
format = pattern_tag.sub(insert_element_code, format_template)
return (format, errors_)
def eval_format_element(format_element, bfo, parameters={}, verbose=0):
"""
Returns the result of the evaluation of the given format element
name, with given BibFormatObject and parameters. Also returns
the errors of the evaluation.
@param format_element: a format element structure as returned by get_format_element
@param bfo: a BibFormatObject used for formatting
@param parameters: a dict of parameters to be used for formatting. Key is parameter and value is value of parameter
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings,
9: errors and warnings, stop if error (debug mode ))
@return: tuple (result, errors)
"""
errors = []
#Load special values given as parameters
prefix = parameters.get('prefix', "")
suffix = parameters.get('suffix', "")
default_value = parameters.get('default', "")
escape = parameters.get('escape', "")
output_text = ''
# 3 possible cases:
# a) format element file is found: we execute it
# b) format element file is not found, but exist in tag table (e.g. bfe_isbn)
# c) format element is totally unknown. Do nothing or report error
if format_element is not None and format_element['type'] == "python":
# a) We found an element with the tag name, of type "python"
# Prepare a dict 'params' to pass as parameter to 'format'
# function of element
params = {}
# Look for parameters defined in format element
# Fill them with specified default values and values
# given as parameters.
# Also remember if the element overrides the 'escape'
# parameter
format_element_overrides_escape = False
for param in format_element['attrs']['params']:
name = param['name']
default = param['default']
params[name] = parameters.get(name, default)
if name == 'escape':
format_element_overrides_escape = True
# Add BibFormatObject
params['bfo'] = bfo
# Execute function with given parameters and return result.
function = format_element['code']
try:
output_text = apply(function, (), params)
except Exception, e:
name = format_element['attrs']['name']
error = ("ERR_BIBFORMAT_EVALUATING_ELEMENT", name, str(params))
errors.append(error)
if verbose == 0:
register_errors(errors, 'error')
elif verbose >= 5:
tb = sys.exc_info()[2]
error_string = get_msgs_for_code_list(error,
stream='error',
ln=CFG_SITE_LANG)
stack = traceback.format_exception(Exception, e, tb, limit=None)
output_text = ''+ \
str(error_string[0][1]) + "".join(stack) +' '
# None can be returned when evaluating function
if output_text is None:
output_text = ""
else:
output_text = str(output_text)
# Escaping:
# (1) By default, everything is escaped in mode 1
# (2) If evaluated element has 'escape_values()' function, use
# its returned value as escape mode, and override (1)
# (3) If template has a defined parameter 'escape' (in allowed
# values), use it, and override (1) and (2). If this
# 'escape' parameter is overriden by the format element
# (defined in the 'format' function of the element), leave
# the escaping job to this element
# (1)
escape_mode = 1
# (2)
escape_function = format_element['escape_function']
if escape_function is not None:
try:
escape_mode = apply(escape_function, (), {'bfo': bfo})
except Exception, e:
error = ("ERR_BIBFORMAT_EVALUATING_ELEMENT_ESCAPE", name)
errors.append(error)
if verbose == 0:
register_errors(errors, 'error')
elif verbose >= 5:
tb = sys.exc_info()[2]
error_string = get_msgs_for_code_list(error,
stream='error',
ln=CFG_SITE_LANG)
output_text += ''+ \
str(error_string[0][1]) +' '
# (3)
if escape in ['0', '1', '2', '3', '4', '5', '6']:
escape_mode = int(escape)
# If escape is equal to 1, then escape all
# HTML reserved chars.
if escape_mode > 0 and not format_element_overrides_escape:
output_text = escape_field(output_text, mode=escape_mode)
# Add prefix and suffix if they have been given as parameters and if
# the evaluation of element is not empty
if output_text.strip() != "":
output_text = prefix + output_text + suffix
# Add the default value if output_text is empty
if output_text == "":
output_text = default_value
return (output_text, errors)
elif format_element is not None and format_element['type'] == "field":
# b) We have not found an element in files that has the tag
# name. Then look for it in the table "tag"
#
#
#
# Load special values given as parameters
separator = parameters.get('separator ', "")
nbMax = parameters.get('nbMax', "")
escape = parameters.get('escape', "1") # By default, escape here
# Get the fields tags that have to be printed
tags = format_element['attrs']['tags']
output_text = []
# Get values corresponding to tags
for tag in tags:
p_tag = parse_tag(tag)
values = record_get_field_values(bfo.get_record(),
p_tag[0],
p_tag[1],
p_tag[2],
p_tag[3])
if len(values)>0 and isinstance(values[0], dict):
#flatten dict to its values only
values_list = map(lambda x: x.values(), values)
#output_text.extend(values)
for values in values_list:
output_text.extend(values)
else:
output_text.extend(values)
if nbMax != "":
try:
nbMax = int(nbMax)
output_text = output_text[:nbMax]
except:
name = format_element['attrs']['name']
error = ("ERR_BIBFORMAT_NBMAX_NOT_INT", name)
errors.append(error)
if verbose < 5:
register_errors(error, 'error')
elif verbose >= 5:
error_string = get_msgs_for_code_list(error,
stream='error',
ln=CFG_SITE_LANG)
output_text = output_text.append(error_string[0][1])
# Add prefix and suffix if they have been given as parameters and if
# the evaluation of element is not empty.
# If evaluation is empty string, return default value if it exists.
# Else return empty string
if ("".join(output_text)).strip() != "":
# If escape is equal to 1, then escape all
# HTML reserved chars.
if escape == '1':
output_text = cgi.escape(separator.join(output_text))
else:
output_text = separator.join(output_text)
output_text = prefix + output_text + suffix
else:
#Return default value
output_text = default_value
return (output_text, errors)
else:
# c) Element is unknown
error = get_msgs_for_code_list([("ERR_BIBFORMAT_CANNOT_RESOLVE_ELEMENT_NAME", format_element)],
stream='error', ln=CFG_SITE_LANG)
errors.append(error)
if verbose < 5:
register_errors(error, 'error')
return ("", errors)
elif verbose >= 5:
if verbose >= 9:
sys.exit(error[0][1])
return ('' + \
error[0][1]+'', errors)
def filter_languages(format_template, ln='en'):
"""
Filters the language tags that do not correspond to the specified language.
@param format_template: the format template code
@param ln: the language that is NOT filtered out from the template
@return: the format template with unnecessary languages filtered out
"""
# First define search_lang_tag(match) and clean_language_tag(match), used
# in re.sub() function
def search_lang_tag(match):
"""
Searches for the ... tag and remove inner localized tags
such as , , that are not current_lang.
If current_lang cannot be found inside ... , try to use 'CFG_SITE_LANG'
@param match: a match object corresponding to the special tag that must be interpreted
"""
current_lang = ln
def clean_language_tag(match):
"""
Return tag text content if tag language of match is output language.
Called by substitution in 'filter_languages(...)'
@param match: a match object corresponding to the special tag that must be interpreted
"""
if match.group(1) == current_lang:
return match.group(2)
else:
return ""
# End of clean_language_tag
lang_tag_content = match.group("langs")
# Try to find tag with current lang. If it does not exists,
# then current_lang becomes CFG_SITE_LANG until the end of this
# replace
pattern_current_lang = re.compile(r"<("+current_lang+ \
r")\s*>(.*?)("+current_lang+r"\s*>)", re.IGNORECASE | re.DOTALL)
if re.search(pattern_current_lang, lang_tag_content) is None:
current_lang = CFG_SITE_LANG
cleaned_lang_tag = ln_pattern.sub(clean_language_tag, lang_tag_content)
return cleaned_lang_tag
# End of search_lang_tag
filtered_format_template = pattern_lang.sub(search_lang_tag, format_template)
return filtered_format_template
def get_format_template(filename, with_attributes=False):
"""
Returns the structured content of the given formate template.
if 'with_attributes' is true, returns the name and description. Else 'attrs' is not
returned as key in dictionary (it might, if it has already been loaded previously)
{'code':"Some template code"
'attrs': {'name': "a name", 'description': "a description"}
}
@param filename: the filename of an format template
@param with_attributes: if True, fetch the attributes (names and description) for format'
@return: strucured content of format template
"""
# Get from cache whenever possible
global format_templates_cache
if not filename.endswith("."+CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION) and \
not filename.endswith(".xsl"):
return None
if format_templates_cache.has_key(filename):
# If we must return with attributes and template exist in
# cache with attributes then return cache.
# Else reload with attributes
if with_attributes and \
format_templates_cache[filename].has_key('attrs'):
return format_templates_cache[filename]
format_template = {'code':""}
try:
path = "%s%s%s" % (CFG_BIBFORMAT_TEMPLATES_PATH, os.sep, filename)
format_file = open(path)
format_content = format_file.read()
format_file.close()
# Load format template code
# Remove name and description
if filename.endswith("."+CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION):
code_and_description = pattern_format_template_name.sub("",
format_content, 1)
code = pattern_format_template_desc.sub("", code_and_description, 1)
else:
code = format_content
format_template['code'] = code
except Exception, e:
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_CANNOT_READ_TEMPLATE_FILE", filename, str(e))],
stream='error', ln=CFG_SITE_LANG)
register_errors(errors, 'error')
# Save attributes if necessary
if with_attributes:
format_template['attrs'] = get_format_template_attrs(filename)
# Cache and return
format_templates_cache[filename] = format_template
return format_template
def get_format_templates(with_attributes=False):
"""
Returns the list of all format templates, as dictionary with filenames as keys
if 'with_attributes' is true, returns the name and description. Else 'attrs' is not
returned as key in each dictionary (it might, if it has already been loaded previously)
[{'code':"Some template code"
'attrs': {'name': "a name", 'description': "a description"}
},
...
}
@param with_attributes: if True, fetch the attributes (names and description) for formats
"""
format_templates = {}
files = os.listdir(CFG_BIBFORMAT_TEMPLATES_PATH)
for filename in files:
if filename.endswith("."+CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION) or \
filename.endswith(".xsl"):
format_templates[filename] = get_format_template(filename,
with_attributes)
return format_templates
def get_format_template_attrs(filename):
"""
Returns the attributes of the format template with given filename
The attributes are {'name', 'description'}
Caution: the function does not check that path exists or
that the format element is valid.
@param the: path to a format element
"""
attrs = {}
attrs['name'] = ""
attrs['description'] = ""
try:
template_file = open("%s%s%s" % (CFG_BIBFORMAT_TEMPLATES_PATH,
os.sep,
filename))
code = template_file.read()
template_file.close()
match = None
if filename.endswith(".xsl"):
# .xsl
attrs['name'] = filename[:-4]
else:
# .bft
match = pattern_format_template_name.search(code)
if match is not None:
attrs['name'] = match.group('name')
else:
attrs['name'] = filename
match = pattern_format_template_desc.search(code)
if match is not None:
attrs['description'] = match.group('desc').rstrip('.')
except Exception, e:
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_CANNOT_READ_TEMPLATE_FILE",
filename, str(e))],
stream='error', ln=CFG_SITE_LANG)
register_errors(errors, 'error')
attrs['name'] = filename
return attrs
def get_format_element(element_name, verbose=0, with_built_in_params=False):
"""
Returns the format element structured content.
Return None if element cannot be loaded (file not found, not readable or
invalid)
The returned structure is {'attrs': {some attributes in dict. See get_format_element_attrs_from_*}
'code': the_function_code,
'type':"field" or "python" depending if element is defined in file or table,
'escape_function': the function to call to know if element output must be escaped}
@param element_name: the name of the format element to load
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings,
9: errors and warnings, stop if error (debug mode ))
@param with_built_in_params: if True, load the parameters built in all elements
@return: a dictionary with format element attributes
"""
# Get from cache whenever possible
global format_elements_cache
errors = []
# Resolve filename and prepare 'name' as key for the cache
filename = resolve_format_element_filename(element_name)
if filename is not None:
name = filename.upper()
else:
name = element_name.upper()
if format_elements_cache.has_key(name):
element = format_elements_cache[name]
if not with_built_in_params or \
(with_built_in_params and \
element['attrs'].has_key('builtin_params')):
return element
if filename is None:
# Element is maybe in tag table
if bibformat_dblayer.tag_exists_for_name(element_name):
format_element = {'attrs': get_format_element_attrs_from_table( \
element_name,
with_built_in_params),
'code':None,
'escape_function':None,
'type':"field"}
# Cache and returns
format_elements_cache[name] = format_element
return format_element
else:
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_FORMAT_ELEMENT_NOT_FOUND",
element_name)],
stream='error', ln=CFG_SITE_LANG)
if verbose == 0:
register_errors(errors, 'error')
elif verbose >= 5:
sys.stderr.write(errors[0][1])
return None
else:
format_element = {}
module_name = filename
if module_name.endswith(".py"):
module_name = module_name[:-3]
# Load element
try:
module = __import__(CFG_BIBFORMAT_ELEMENTS_IMPORT_PATH + \
"." + module_name)
# Load last module in import path
# For eg. load bfe_name in
# invenio.bibformat_elements.bfe_name
# Used to keep flexibility regarding where elements
# directory is (for eg. test cases)
components = CFG_BIBFORMAT_ELEMENTS_IMPORT_PATH.split(".")
for comp in components[1:]:
module = getattr(module, comp)
except Exception, e:
# We catch all exceptions here, as we just want to print
# traceback in all cases
tb = sys.exc_info()[2]
stack = traceback.format_exception(Exception, e, tb, limit=None)
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_IN_FORMAT_ELEMENT",
element_name,"\n" + "\n".join(stack[-2:-1]))],
stream='error', ln=CFG_SITE_LANG)
if verbose == 0:
register_errors(errors, 'error')
elif verbose >= 5:
sys.stderr.write(errors[0][1])
if errors:
if verbose >= 7:
raise Exception, errors[0][1]
return None
# Load function 'format_element()' inside element
try:
function_format = module.__dict__[module_name].format_element
format_element['code'] = function_format
except AttributeError, e:
# Try to load 'format()' function
try:
function_format = module.__dict__[module_name].format
format_element['code'] = function_format
except AttributeError, e:
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_FORMAT_ELEMENT_FORMAT_FUNCTION",
element_name)],
stream='error', ln=CFG_SITE_LANG)
if verbose == 0:
register_errors(errors, 'error')
elif verbose >= 5:
sys.stderr.write(errors[0][1])
if errors:
if verbose >= 7:
raise Exception, errors[0][1]
return None
# Load function 'escape_values()' inside element
function_escape = getattr(module.__dict__[module_name],
'escape_values',
None)
format_element['escape_function'] = function_escape
# Prepare, cache and return
format_element['attrs'] = get_format_element_attrs_from_function( \
function_format,
element_name,
with_built_in_params)
format_element['type'] = "python"
format_elements_cache[name] = format_element
return format_element
def get_format_elements(with_built_in_params=False):
"""
Returns the list of format elements attributes as dictionary structure
Elements declared in files have priority over element declared in 'tag' table
The returned object has this format:
{element_name1: {'attrs': {'description':..., 'seealso':...
'params':[{'name':..., 'default':..., 'description':...}, ...]
'builtin_params':[{'name':..., 'default':..., 'description':...}, ...]
},
'code': code_of_the_element
},
element_name2: {...},
...}
Returns only elements that could be loaded (not error in code)
@return: a dict of format elements with name as key, and a dict as attributes
@param with_built_in_params: if True, load the parameters built in all elements
"""
format_elements = {}
mappings = bibformat_dblayer.get_all_name_tag_mappings()
for name in mappings:
format_elements[name.upper().replace(" ", "_").strip()] = get_format_element(name, with_built_in_params=with_built_in_params)
files = os.listdir(CFG_BIBFORMAT_ELEMENTS_PATH)
for filename in files:
filename_test = filename.upper().replace(" ", "_")
if filename_test.endswith(".PY") and filename.upper() != "__INIT__.PY":
if filename_test.startswith("BFE_"):
filename_test = filename_test[4:]
element_name = filename_test[:-3]
element = get_format_element(element_name,
with_built_in_params=with_built_in_params)
if element is not None:
format_elements[element_name] = element
return format_elements
def get_format_element_attrs_from_function(function, element_name,
with_built_in_params=False):
""" Returns the attributes of the
function given as parameter.
It looks for standard parameters of the function, default
values and comments in the docstring.
The attributes are {'description', 'seealso':['element.py', ...],
'params':{name:{'name', 'default', 'description'}, ...], name2:{}}
The attributes are {'name' : "name of element" #basically the name of 'name' parameter
'description': "a string description of the element",
'seealso' : ["element_1.py", "element_2.py", ...] #a list of related elements
'params': [{'name':"param_name", #a list of parameters for this element (except 'bfo')
'default':"default value",
'description': "a description"}, ...],
'builtin_params': {name: {'name':"param_name",#the parameters builtin for all elem of this kind
'default':"default value",
'description': "a description"}, ...},
}
@param function: the formatting function of a format element
@param element_name: the name of the element
@param with_built_in_params: if True, load the parameters built in all elements
"""
attrs = {}
attrs['description'] = ""
attrs['name'] = element_name.replace(" ", "_").upper()
attrs['seealso'] = []
docstring = function.__doc__
if isinstance(docstring, str):
# Look for function description in docstring
#match = pattern_format_element_desc.search(docstring)
description = docstring.split("@param")[0]
description = description.split("@see:")[0]
attrs['description'] = description.strip().rstrip('.')
# Look for @see: in docstring
match = pattern_format_element_seealso.search(docstring)
if match is not None:
elements = match.group('see').rstrip('.').split(",")
for element in elements:
attrs['seealso'].append(element.strip())
params = {}
# Look for parameters in function definition
(args, varargs, varkw, defaults) = inspect.getargspec(function)
# Prepare args and defaults_list such that we can have a mapping
# from args to defaults
args.reverse()
if defaults is not None:
defaults_list = list(defaults)
defaults_list.reverse()
else:
defaults_list = []
for arg, default in map(None, args, defaults_list):
if arg == "bfo":
#Don't keep this as parameter. It is hidden to users, and
#exists in all elements of this kind
continue
param = {}
param['name'] = arg
if default is None:
#In case no check is made inside element, we prefer to
#print "" (nothing) than None in output
param['default'] = ""
else:
param['default'] = default
param['description'] = "(no description provided)"
params[arg] = param
if isinstance(docstring, str):
# Look for AT param descriptions in docstring.
# Add description to existing parameters in params dict
params_iterator = pattern_format_element_params.finditer(docstring)
for match in params_iterator:
name = match.group('name')
if params.has_key(name):
params[name]['description'] = match.group('desc').rstrip('.')
attrs['params'] = params.values()
# Load built-in parameters if necessary
if with_built_in_params:
builtin_params = []
# Add 'prefix' parameter
param_prefix = {}
param_prefix['name'] = "prefix"
param_prefix['default'] = ""
param_prefix['description'] = """A prefix printed only if the
record has a value for this element"""
builtin_params.append(param_prefix)
# Add 'suffix' parameter
param_suffix = {}
param_suffix['name'] = "suffix"
param_suffix['default'] = ""
param_suffix['description'] = """A suffix printed only if the
record has a value for this element"""
builtin_params.append(param_suffix)
# Add 'default' parameter
param_default = {}
param_default['name'] = "default"
param_default['default'] = ""
param_default['description'] = """A default value printed if the
record has no value for this element"""
builtin_params.append(param_default)
# Add 'escape' parameter
param_escape = {}
param_escape['name'] = "escape"
param_escape['default'] = ""
param_escape['description'] = """0 keeps value as it is. Refers to main
documentation for escaping modes
1 to 6"""
builtin_params.append(param_escape)
attrs['builtin_params'] = builtin_params
return attrs
def get_format_element_attrs_from_table(element_name,
with_built_in_params=False):
"""
Returns the attributes of the format element with given name in 'tag' table.
Returns None if element_name does not exist in tag table.
The attributes are {'name' : "name of element" #basically the name of 'element_name' parameter
'description': "a string description of the element",
'seealso' : [] #a list of related elements. Always empty in this case
'params': [], #a list of parameters for this element. Always empty in this case
'builtin_params': [{'name':"param_name", #the parameters builtin for all elem of this kind
'default':"default value",
'description': "a description"}, ...],
'tags':["950.1", 203.a] #the list of tags printed by this element
}
@param element_name: an element name in database
@param element_name: the name of the element
@param with_built_in_params: if True, load the parameters built in all elements
"""
attrs = {}
tags = bibformat_dblayer.get_tags_from_name(element_name)
field_label = "field"
if len(tags)>1:
field_label = "fields"
attrs['description'] = "Prints %s %s of the record" % (field_label,
", ".join(tags))
attrs['name'] = element_name.replace(" ", "_").upper()
attrs['seealso'] = []
attrs['params'] = []
attrs['tags'] = tags
# Load built-in parameters if necessary
if with_built_in_params:
builtin_params = []
# Add 'prefix' parameter
param_prefix = {}
param_prefix['name'] = "prefix"
param_prefix['default'] = ""
param_prefix['description'] = """A prefix printed only if the
record has a value for this element"""
builtin_params.append(param_prefix)
# Add 'suffix' parameter
param_suffix = {}
param_suffix['name'] = "suffix"
param_suffix['default'] = ""
param_suffix['description'] = """A suffix printed only if the
record has a value for this element"""
builtin_params.append(param_suffix)
# Add 'separator' parameter
param_separator = {}
param_separator['name'] = "separator"
param_separator['default'] = " "
param_separator['description'] = """A separator between elements of
the field"""
builtin_params.append(param_separator)
# Add 'nbMax' parameter
param_nbMax = {}
param_nbMax['name'] = "nbMax"
param_nbMax['default'] = ""
param_nbMax['description'] = """The maximum number of values to
print for this element. No limit if not
specified"""
builtin_params.append(param_nbMax)
# Add 'default' parameter
param_default = {}
param_default['name'] = "default"
param_default['default'] = ""
param_default['description'] = """A default value printed if the
record has no value for this element"""
builtin_params.append(param_default)
# Add 'escape' parameter
param_escape = {}
param_escape['name'] = "escape"
param_escape['default'] = ""
param_escape['description'] = """If set to 1, replaces special
characters '&', '<' and '>' of this
element by SGML entities"""
builtin_params.append(param_escape)
attrs['builtin_params'] = builtin_params
return attrs
def get_output_format(code, with_attributes=False, verbose=0):
"""
Returns the structured content of the given output format
If 'with_attributes' is true, also returns the names and description of the output formats,
else 'attrs' is not returned in dict (it might, if it has already been loaded previously).
if output format corresponding to 'code' is not found return an empty structure.
See get_output_format_attrs() to learn more on the attributes
{'rules': [ {'field': "980__a",
'value': "PREPRINT",
'template': "filename_a.bft",
},
{...}
],
'attrs': {'names': {'generic':"a name", 'sn':{'en': "a name", 'fr':"un nom"}, 'ln':{'en':"a long name"}}
'description': "a description"
'code': "fnm1",
'content_type': "application/ms-excel",
'visibility': 1
}
'default':"filename_b.bft"
}
@param code: the code of an output_format
@param with_attributes: if True, fetch the attributes (names and description) for format
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings,
9: errors and warnings, stop if error (debug mode ))
@return: strucured content of output format
"""
output_format = {'rules':[], 'default':""}
filename = resolve_output_format_filename(code, verbose)
if filename is None:
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_OUTPUT_FORMAT_CODE_UNKNOWN", code)],
stream='error', ln=CFG_SITE_LANG)
register_errors(errors, 'error')
if with_attributes: #Create empty attrs if asked for attributes
output_format['attrs'] = get_output_format_attrs(code, verbose)
return output_format
# Get from cache whenever possible
global format_outputs_cache
if format_outputs_cache.has_key(filename):
# If was must return with attributes but cache has not
# attributes, then load attributes
if with_attributes and not \
format_outputs_cache[filename].has_key('attrs'):
format_outputs_cache[filename]['attrs'] = get_output_format_attrs(code, verbose)
return format_outputs_cache[filename]
try:
if with_attributes:
output_format['attrs'] = get_output_format_attrs(code, verbose)
path = "%s%s%s" % (CFG_BIBFORMAT_OUTPUTS_PATH, os.sep, filename )
format_file = open(path)
current_tag = ''
for line in format_file:
line = line.strip()
if line == "":
# Ignore blank lines
continue
if line.endswith(":"):
# Retrieve tag
# Remove : spaces and eol at the end of line
clean_line = line.rstrip(": \n\r")
# The tag starts at second position
current_tag = "".join(clean_line.split()[1:]).strip()
elif line.find('---') != -1:
words = line.split('---')
template = words[-1].strip()
condition = ''.join(words[:-1])
value = ""
output_format['rules'].append({'field': current_tag,
'value': condition,
'template': template,
})
elif line.find(':') != -1:
# Default case
default = line.split(':')[1].strip()
output_format['default'] = default
except Exception, e:
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_CANNOT_READ_OUTPUT_FILE", filename, str(e))],
stream='error', ln=CFG_SITE_LANG)
register_errors(errors, 'error')
# Cache and return
format_outputs_cache[filename] = output_format
return output_format
def get_output_format_attrs(code, verbose=0):
"""
Returns the attributes of an output format.
The attributes contain 'code', which is the short identifier of the output format
(to be given as parameter in format_record function to specify the output format),
'description', a description of the output format, 'visibility' the visibility of
the format in the output format list on public pages and 'names', the localized names
of the output format. If 'content_type' is specified then the search_engine will
send a file with this content type and with result of formatting as content to the user.
The 'names' dict always contais 'generic', 'ln' (for long name) and 'sn' (for short names)
keys. 'generic' is the default name for output format. 'ln' and 'sn' contain long and short
localized names of the output format. Only the languages for which a localization exist
are used.
{'names': {'generic':"a name", 'sn':{'en': "a name", 'fr':"un nom"}, 'ln':{'en':"a long name"}}
'description': "a description"
'code': "fnm1",
'content_type': "application/ms-excel",
'visibility': 1
}
@param code: the short identifier of the format
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings,
9: errors and warnings, stop if error (debug mode ))
@return: strucured content of output format attributes
"""
if code.endswith("."+CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION):
code = code[:-(len(CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION) + 1)]
attrs = {'names':{'generic':"",
'ln':{},
'sn':{}},
'description':'',
'code':code.upper(),
'content_type':"",
'visibility':1}
filename = resolve_output_format_filename(code, verbose)
if filename is None:
return attrs
attrs['names'] = bibformat_dblayer.get_output_format_names(code)
attrs['description'] = bibformat_dblayer.get_output_format_description(code)
attrs['content_type'] = bibformat_dblayer.get_output_format_content_type(code)
attrs['visibility'] = bibformat_dblayer.get_output_format_visibility(code)
return attrs
def get_output_formats(with_attributes=False):
"""
Returns the list of all output format, as a dictionary with their filename as key
If 'with_attributes' is true, also returns the names and description of the output formats,
else 'attrs' is not returned in dicts (it might, if it has already been loaded previously).
See get_output_format_attrs() to learn more on the attributes
{'filename_1.bfo': {'rules': [ {'field': "980__a",
'value': "PREPRINT",
'template': "filename_a.bft",
},
{...}
],
'attrs': {'names': {'generic':"a name", 'sn':{'en': "a name", 'fr':"un nom"}, 'ln':{'en':"a long name"}}
'description': "a description"
'code': "fnm1"
}
'default':"filename_b.bft"
},
'filename_2.bfo': {...},
...
}
@return: the list of output formats
"""
output_formats = {}
files = os.listdir(CFG_BIBFORMAT_OUTPUTS_PATH)
for filename in files:
if filename.endswith("."+CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION):
code = "".join(filename.split(".")[:-1])
output_formats[filename] = get_output_format(code, with_attributes)
return output_formats
def resolve_format_element_filename(string):
"""
Returns the filename of element corresponding to string
This is necessary since format templates code call
elements by ignoring case, for eg. is the
same as .
It is also recommended that format elements filenames are
prefixed with bfe_ . We need to look for these too.
The name of the element has to start with "BFE_".
@param name: a name for a format element
@return: the corresponding filename, with right case
"""
if not string.endswith(".py"):
name = string.replace(" ", "_").upper() +".PY"
else:
name = string.replace(" ", "_").upper()
files = os.listdir(CFG_BIBFORMAT_ELEMENTS_PATH)
for filename in files:
test_filename = filename.replace(" ", "_").upper()
if test_filename == name or \
test_filename == "BFE_" + name or \
"BFE_" + test_filename == name:
return filename
# No element with that name found
# Do not log error, as it might be a normal execution case:
# element can be in database
return None
def resolve_output_format_filename(code, verbose=0):
"""
Returns the filename of output corresponding to code
This is necessary since output formats names are not case sensitive
but most file systems are.
@param code: the code for an output format
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings,
9: errors and warnings, stop if error (debug mode ))
@return: the corresponding filename, with right case, or None if not found
"""
#Remove non alphanumeric chars (except . and _)
code = re.sub(r"[^.0-9a-zA-Z_]", "", code)
if not code.endswith("."+CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION):
code = re.sub(r"\W", "", code)
code += "."+CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION
files = os.listdir(CFG_BIBFORMAT_OUTPUTS_PATH)
for filename in files:
if filename.upper() == code.upper():
return filename
# No output format with that name found
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_CANNOT_RESOLVE_OUTPUT_NAME", code)],
stream='error', ln=CFG_SITE_LANG)
if verbose == 0:
register_errors(errors, 'error')
elif verbose >= 5:
sys.stderr.write(errors[0][1])
if verbose >= 9:
sys.exit(errors[0][1])
return None
def get_fresh_format_template_filename(name):
"""
Returns a new filename and name for template with given name.
Used when writing a new template to a file, so that the name
has no space, is unique in template directory
Returns (unique_filename, modified_name)
@param a: name for a format template
@return: the corresponding filename, and modified name if necessary
"""
#name = re.sub(r"\W", "", name) #Remove non alphanumeric chars
name = name.replace(" ", "_")
filename = name
# Remove non alphanumeric chars (except .)
filename = re.sub(r"[^.0-9a-zA-Z]", "", filename)
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + filename \
+ "." + CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION
index = 1
while os.path.exists(path):
index += 1
filename = name + str(index)
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + filename \
+ "." + CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION
if index > 1:
returned_name = (name + str(index)).replace("_", " ")
else:
returned_name = name.replace("_", " ")
return (filename + "." + CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION,
returned_name) #filename.replace("_", " "))
def get_fresh_output_format_filename(code):
"""
Returns a new filename for output format with given code.
Used when writing a new output format to a file, so that the code
has no space, is unique in output format directory. The filename
also need to be at most 6 chars long, as the convention is that
filename == output format code (+ .extension)
We return an uppercase code
Returns (unique_filename, modified_code)
@param code: the code of an output format
@return: the corresponding filename, and modified code if necessary
"""
#code = re.sub(r"\W", "", code) #Remove non alphanumeric chars
code = code.upper().replace(" ", "_")
# Remove non alphanumeric chars (except . and _)
code = re.sub(r"[^.0-9a-zA-Z_]", "", code)
if len(code) > 6:
code = code[:6]
filename = code
path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + filename \
+ "." + CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION
index = 2
while os.path.exists(path):
filename = code + str(index)
if len(filename) > 6:
filename = code[:-(len(str(index)))]+str(index)
index += 1
path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + filename \
+ "." + CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION
# We should not try more than 99999... Well I don't see how we
# could get there.. Sanity check.
if index >= 99999:
errors = get_msgs_for_code_list([("ERR_BIBFORMAT_NB_OUTPUTS_LIMIT_REACHED", code)],
stream='error', ln=CFG_SITE_LANG)
register_errors(errors, 'error')
sys.exit("Output format cannot be named as %s"%code)
return (filename + "." + CFG_BIBFORMAT_FORMAT_OUTPUT_EXTENSION, filename)
def clear_caches():
"""
Clear the caches (Output Format, Format Templates and Format Elements)
"""
global format_templates_cache, format_elements_cache, format_outputs_cache
format_templates_cache = {}
format_elements_cache = {}
format_outputs_cache = {}
class BibFormatObject:
"""
An object that encapsulates a record and associated methods, and that is given
as parameter to all format elements 'format' function.
The object is made specifically for a given formatting, i.e. it includes
for example the language for the formatting.
The object provides basic accessors to the record. For full access, one can get
the record with get_record() and then use BibRecord methods on the returned object.
"""
# The record
record = None
# The language in which the formatting has to be done
lang = CFG_SITE_LANG
# A list of string describing the context in which the record has
# to be formatted.
# It represents the words of the user request in web interface search
search_pattern = []
# The id of the record
recID = 0
# The information about the user, as returned by
# 'webuser.collect_user_info(req)'
user_info = None
# The format in which the record is being formatted
output_format = ''
req = None # DEPRECATED: use bfo.user_info instead. Used by WebJournal.
def __init__(self, recID, ln=CFG_SITE_LANG, search_pattern=None,
xml_record=None, user_info=None, output_format=''):
"""
Creates a new bibformat object, with given record.
You can either specify an record ID to format, or give its xml representation.
if 'xml_record' is not None, use 'xml_record' instead of recID for the record.
'user_info' allows to grant access to some functionalities on
a page depending on the user's priviledges. It is a dictionary
in the following form:
user_info = {
'remote_ip' : '',
'remote_host' : '',
'referer' : '',
'uri' : '',
'agent' : '',
- 'apache_user' : '',
- 'apache_group' : [],
'uid' : -1,
'nickname' : '',
'email' : '',
'group' : [],
'guest' : '1'
}
@param recID: the id of a record
@param ln: the language in which the record has to be formatted
@param search_pattern: list of string representing the request used by the user in web interface
@param xml_record: a xml string of the record to format
@param user_info: the information of the user who will view the formatted page
@param output_format: the output_format used for formatting this record
"""
if xml_record is not None:
# If record is given as parameter
self.record = create_record(xml_record)[0]
recID = record_get_field_value(self.record, "001")
self.lang = wash_language(ln)
if search_pattern is None:
search_pattern = []
self.search_pattern = search_pattern
self.recID = recID
self.output_format = output_format
self.user_info = user_info
if self.user_info is None:
self.user_info = collect_user_info(None)
def get_record(self):
"""
Returns the record structure of this BibFormatObject instance
@return: the record structure as defined by BibRecord library
"""
from invenio.search_engine import get_record
# Create record if necessary
if self.record is None:
# on-the-fly creation if current output is xm
self.record = get_record(self.recID)
return self.record
def control_field(self, tag, escape=0):
"""
Returns the value of control field given by tag in record
@param tag: the marc code of a field
@param escape: 1 if returned value should be escaped. Else 0.
@return: value of field tag in record
"""
if self.get_record() is None:
#Case where BibRecord could not parse object
return ''
p_tag = parse_tag(tag)
field_value = record_get_field_value(self.get_record(),
p_tag[0],
p_tag[1],
p_tag[2],
p_tag[3])
if escape == 0:
return field_value
else:
return escape_field(field_value, escape)
def field(self, tag, escape=0):
"""
Returns the value of the field corresponding to tag in the
current record.
If the value does not exist, return empty string. Else
returns the same as bfo.fields(..)[0] (see docstring below).
'escape' parameter allows to escape special characters
of the field. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - remove unsafe HTML tags (Eg. keep )
3 - Mix of mode 1 and 2. If value of field starts with
, then use mode 2. Else use mode 1.
4 - Remove all HTML tags
5 - Same as 2, with more tags allowed (like )
6 - Same as 3, with more tags allowed (like )
@param tag: the marc code of a field
@param escape: 1 if returned value should be escaped. Else 0. (see above for other modes)
@return: value of field tag in record
"""
list_of_fields = self.fields(tag)
if len(list_of_fields) > 0:
# Escaping below
if escape == 0:
return list_of_fields[0]
else:
return escape_field(list_of_fields[0], escape)
else:
return ""
def fields(self, tag, escape=0, repeatable_subfields_p=False):
"""
Returns the list of values corresonding to "tag".
If tag has an undefined subcode (such as 999C5),
the function returns a list of dictionaries, whoose keys
are the subcodes and the values are the values of tag.subcode.
If the tag has a subcode, simply returns list of values
corresponding to tag.
Eg. for given MARC:
999C5 $a value_1a $b value_1b
999C5 $b value_2b
999C5 $b value_3b $b value_3b_bis
>> bfo.fields('999C5b')
>> ['value_1b', 'value_2b', 'value_3b', 'value_3b_bis']
>> bfo.fields('999C5')
>> [{'a':'value_1a', 'b':'value_1b'},
{'b':'value_2b'},
{'b':'value_3b'}]
By default the function returns only one value for each
subfield (that is it considers that repeatable subfields are
not allowed). It is why in the above example 'value3b_bis' is
not shown for bfo.fields('999C5'). (Note that it is not
defined which of value_3b or value_3b_bis is returned). This
is to simplify the use of the function, as most of the time
subfields are not repeatable (in that way we get a string
instead of a list). You can allow repeatable subfields by
setting 'repeatable_subfields_p' parameter to True. In
this mode, the above example would return:
>> bfo.fields('999C5b', repeatable_subfields_p=True)
>> ['value_1b', 'value_2b', 'value_3b']
>> bfo.fields('999C5', repeatable_subfields_p=True)
>> [{'a':['value_1a'], 'b':['value_1b']},
{'b':['value_2b']},
{'b':['value_3b', 'value3b_bis']}]
NOTICE THAT THE RETURNED STRUCTURE IS DIFFERENT. Also note
that whatever the value of 'repeatable_subfields_p' is,
bfo.fields('999C5b') always show all fields, even repeatable
ones. This is because the parameter has no impact on the
returned structure (it is always a list).
'escape' parameter allows to escape special characters
of the fields. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - remove unsafe HTML tags (Eg. keep )
3 - Mix of mode 1 and 2. If value of field starts with
, then use mode 2. Else use mode 1.
4 - Remove all HTML tags
5 - Same as 2, with more tags allowed (like )
6 - Same as 3, with more tags allowed (like )
@param tag: the marc code of a field
@param escape: 1 if returned values should be escaped. Else 0.
@repeatable_subfields_p if True, returns the list of subfields in the dictionary
@return: values of field tag in record
"""
if self.get_record() is None:
# Case where BibRecord could not parse object
return []
p_tag = parse_tag(tag)
if p_tag[3] != "":
# Subcode has been defined. Simply returns list of values
values = record_get_field_values(self.get_record(),
p_tag[0],
p_tag[1],
p_tag[2],
p_tag[3])
if escape == 0:
return values
else:
return [escape_field(value, escape) for value in values]
else:
# Subcode is undefined. Returns list of dicts.
# However it might be the case of a control field.
instances = record_get_field_instances(self.get_record(),
p_tag[0],
p_tag[1],
p_tag[2])
if repeatable_subfields_p:
list_of_instances = []
for instance in instances:
instance_dict = {}
for subfield in instance[0]:
if not instance_dict.has_key(subfield[0]):
instance_dict[subfield[0]] = []
if escape == 0:
instance_dict[subfield[0]].append(subfield[1])
else:
instance_dict[subfield[0]].append(escape_field(subfield[1], escape))
list_of_instances.append(instance_dict)
return list_of_instances
else:
if escape == 0:
return [dict(instance[0]) for instance in instances]
else:
return [dict([ (subfield[0], escape_field(subfield[1], escape)) \
for subfield in instance[0] ]) \
for instance in instances]
def kb(self, kb, string, default=""):
"""
Returns the value of the "string" in the knowledge base "kb".
If kb does not exist or string does not exist in kb,
returns 'default' string or empty string if not specified.
@param kb: a knowledge base name
@param string: the string we want to translate
@param default: a default value returned if 'string' not found in 'kb'
"""
if string is None:
return default
val = get_kbr_values(kb, searchkey=string, searchtype='e')
try:
return val[0][0]
except:
return default
def escape_field(value, mode=0):
"""
Utility function used to escape the value of a field in given mode.
- mode 0: no escaping
- mode 1: escaping all HTML/XML characters (escaped chars are shown as escaped)
- mode 2: escaping unsafe HTML tags to avoid XSS, but
keep basic one (such as )
Escaped tags are removed.
- mode 3: mix of mode 1 and mode 2. If field_value starts with ,
then use mode 2. Else use mode 1.
- mode 4: escaping all HTML/XML tags (escaped tags are removed)
- mode 5: same as 2, but allows more tags, like
- mode 6: same as 3, but allows more tags, like
"""
if mode == 1:
return cgi.escape(value)
elif mode in [2, 5]:
allowed_attribute_whitelist = cfg_html_buffer_allowed_attribute_whitelist
allowed_tag_whitelist = cfg_html_buffer_allowed_tag_whitelist + \
('class',)
if mode == 5:
allowed_attribute_whitelist += ('src', 'alt',
'width', 'height',
'style', 'summary',
'border', 'cellspacing',
'cellpadding')
allowed_tag_whitelist += ('img', 'table', 'td',
'tr', 'th', 'span', 'caption')
try:
return washer.wash(value,
allowed_attribute_whitelist=\
allowed_attribute_whitelist,
allowed_tag_whitelist= \
allowed_tag_whitelist
)
except HTMLParseError:
# Parsing failed
return cgi.escape(value)
elif mode in [3, 6]:
if value.lstrip(' \n').startswith(html_field):
allowed_attribute_whitelist = cfg_html_buffer_allowed_attribute_whitelist
allowed_tag_whitelist = cfg_html_buffer_allowed_tag_whitelist + \
('class',)
if mode == 6:
allowed_attribute_whitelist += ('src', 'alt',
'width', 'height',
'style', 'summary',
'border', 'cellspacing',
'cellpadding')
allowed_tag_whitelist += ('img', 'table', 'td',
'tr', 'th', 'span', 'caption')
try:
return washer.wash(value,
allowed_attribute_whitelist=\
allowed_attribute_whitelist,
allowed_tag_whitelist=\
allowed_tag_whitelist
)
except HTMLParseError:
# Parsing failed
return cgi.escape(value)
else:
return cgi.escape(value)
elif mode == 4:
try:
return washer.wash(value,
allowed_attribute_whitelist=[],
allowed_tag_whitelist=[]
)
except HTMLParseError:
# Parsing failed
return cgi.escape(value)
else:
return value
def bf_profile():
"""
Runs a benchmark
"""
for i in range(1, 51):
format_record(i, "HD", ln=CFG_SITE_LANG, verbose=9, search_pattern=[])
return
if __name__ == "__main__":
import profile
import pstats
#bf_profile()
profile.run('bf_profile()', "bibformat_profile")
p = pstats.Stats("bibformat_profile")
p.strip_dirs().sort_stats("cumulative").print_stats()
diff --git a/modules/bibsched/lib/bibtask.py b/modules/bibsched/lib/bibtask.py
index 86db3dda4..7e1bc64b2 100644
--- a/modules/bibsched/lib/bibtask.py
+++ b/modules/bibsched/lib/bibtask.py
@@ -1,986 +1,986 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio Bibliographic Task Class.
BibTask class.
A BibTask is an executable under CFG_BINDIR, whose name is stored in
bibtask_config.CFG_BIBTASK_VALID_TASKS.
A valid task must call the task_init function with the proper parameters.
Generic task related parameters (user, sleeptime, runtime, task_id, task_name
verbose)
go to _TASK_PARAMS global dictionary accessible through task_get_task_param.
Option specific to the particular BibTask go to _OPTIONS global dictionary
and are accessible via task_get_option/task_set_option.
In order to log something properly, just use write_message(s) with the desired
verbose level.
task_update_status and task_update_progress can be used to update the status
of the task (DONE, FAILED, DONE WITH ERRORS...) and it's progress
(1 out 100..) within the bibsched monitor.
It is possible to enqueue a BibTask via API call by means of
task_low_level_submission.
"""
__revision__ = "$Id$"
import getopt
import getpass
import marshal
import os
import pwd
import re
import signal
import sys
import time
import datetime
import traceback
import logging
import logging.handlers
from invenio.dbquery import run_sql, _db_login
from invenio.access_control_engine import acc_authorize_action
from invenio.config import CFG_PREFIX, CFG_BINDIR, CFG_LOGDIR, \
CFG_BIBSCHED_PROCESS_USER, CFG_TMPDIR
from invenio.errorlib import register_exception
from invenio.access_control_config import CFG_EXTERNAL_AUTH_USING_SSO, \
CFG_EXTERNAL_AUTHENTICATION
from invenio.webuser import get_user_preferences, get_email
from invenio.bibtask_config import CFG_BIBTASK_VALID_TASKS, \
CFG_BIBTASK_DEFAULT_TASK_SETTINGS
# Global _TASK_PARAMS dictionary.
_TASK_PARAMS = {
'version': '',
'task_stop_helper_fnc': None,
'task_name': os.path.basename(sys.argv[0]),
'task_specific_name': '',
'user': '',
# If the task is not initialized (usually a developer debugging
# a single method), output all messages.
'verbose': 9,
'sleeptime': '',
'runtime': time.strftime("%Y-%m-%d %H:%M:%S"),
'priority': 0,
'runtime_limit': None,
'profile': [],
}
# Global _OPTIONS dictionary.
_OPTIONS = {}
# Which tasks don't need to ask the user for authorization?
CFG_VALID_PROCESSES_NO_AUTH_NEEDED = ("bibupload", )
CFG_TASK_IS_NOT_A_DEAMON = ("bibupload", )
def fix_argv_paths(paths, argv=None):
"""Given the argv vector of cli parameters, and a list of path that
can be relative and may have been specified within argv,
it substitute all the occurencies of these paths in argv.
argv is changed in place and returned.
"""
if argv is None:
argv = sys.argv
for path in paths:
for count in xrange(len(argv)):
if path == argv[count]:
argv[count] = os.path.abspath(path)
return argv
def task_low_level_submission(name, user, *argv):
"""Let special lowlevel enqueuing of a task on the bibsche queue.
@param name: is the name of the bibtask. It must be a valid executable under
C{CFG_BINDIR}.
@type name: string
@param user: is a string that will appear as the "user" submitting the task.
Since task are submitted via API it make sense to set the
user to the name of the module/function that called
task_low_level_submission.
@type user: string
@param argv: are all the additional CLI parameters that would have been
passed on the CLI (one parameter per variable).
e.g.:
>>> task_low_level_submission('bibupload', 'admin', '-a', '/tmp/z.xml')
@type: strings
@return: the task identifier when the task is correctly enqueued.
@rtype: int
@note: use absolute paths in argv
"""
def get_priority(argv):
"""Try to get the priority by analysing the arguments."""
priority = 0
argv = list(argv)
while True:
try:
opts, args = getopt.gnu_getopt(argv, 'P:', ['priority='])
except getopt.GetoptError, err:
## We remove one by one all the non recognized parameters
if len(err.opt) > 1:
argv = [arg for arg in argv if arg != '--%s' % err.opt and not arg.startswith('--%s=' % err.opt)]
else:
argv = [arg for arg in argv if not arg.startswith('-%s' % err.opt)]
else:
break
for opt in opts:
if opt[0] in ('-P', '--priority'):
try:
priority = int(opt[1])
except ValueError:
pass
return priority
def get_special_name(argv):
"""Try to get the special name by analysing the arguments."""
special_name = ''
argv = list(argv)
while True:
try:
opts, args = getopt.gnu_getopt(argv, 'N:', ['name='])
except getopt.GetoptError, err:
## We remove one by one all the non recognized parameters
if len(err.opt) > 1:
argv = [arg for arg in argv if arg != '--%s' % err.opt and not arg.startswith('--%s=' % err.opt)]
else:
argv = [arg for arg in argv if not arg.startswith('-%s' % err.opt)]
else:
break
for opt in opts:
if opt[0] in ('-N', '--name'):
special_name = opt[1]
return special_name
task_id = None
try:
if not name in CFG_BIBTASK_VALID_TASKS:
raise StandardError('%s is not a valid task name' % name)
priority = get_priority(argv)
special_name = get_special_name(argv)
argv = tuple([os.path.join(CFG_BINDIR, name)] + list(argv))
if special_name:
name = '%s:%s' % (name, special_name)
## submit task:
task_id = run_sql("""INSERT INTO schTASK (proc,user,
runtime,sleeptime,status,progress,arguments,priority)
VALUES (%s,%s,NOW(),'','WAITING','',%s,%s)""",
(name, user, marshal.dumps(argv), priority))
except Exception:
register_exception(alert_admin=True)
if task_id:
run_sql("""DELETE FROM schTASK WHERE id=%s""", (task_id, ))
raise
return task_id
def setup_loggers(task_id=None):
"""Sets up the logging system."""
logger = logging.getLogger()
for handler in logger.handlers:
## Let's clean the handlers in case some piece of code has already
## fired any write_message, i.e. any call to debug, info, etc.
## which triggered a call to logging.basicConfig()
logger.removeHandler(handler)
formatter = logging.Formatter('%(asctime)s --> %(message)s', '%Y-%m-%d %H:%M:%S')
if task_id is not None:
err_logger = logging.handlers.RotatingFileHandler(os.path.join(CFG_LOGDIR, 'bibsched_task_%d.err' % _TASK_PARAMS['task_id']), 'a', 1*1024*1024, 10)
log_logger = logging.handlers.RotatingFileHandler(os.path.join(CFG_LOGDIR, 'bibsched_task_%d.log' % _TASK_PARAMS['task_id']), 'a', 1*1024*1024, 10)
log_logger.setFormatter(formatter)
log_logger.setLevel(logging.DEBUG)
err_logger.setFormatter(formatter)
err_logger.setLevel(logging.WARNING)
logger.addHandler(err_logger)
logger.addHandler(log_logger)
stdout_logger = logging.StreamHandler(sys.stdout)
stdout_logger.setFormatter(formatter)
stdout_logger.setLevel(logging.DEBUG)
stderr_logger = logging.StreamHandler(sys.stderr)
stderr_logger.setFormatter(formatter)
stderr_logger.setLevel(logging.WARNING)
logger.addHandler(stderr_logger)
logger.addHandler(stdout_logger)
logger.setLevel(logging.INFO)
return logger
def task_init(
authorization_action="",
authorization_msg="",
description="",
help_specific_usage="",
version=__revision__,
specific_params=("", []),
task_stop_helper_fnc=None,
task_submit_elaborate_specific_parameter_fnc=None,
task_submit_check_options_fnc=None,
task_run_fnc=None):
""" Initialize a BibTask.
@param authorization_action: is the name of the authorization action
connected with this task;
@param authorization_msg: is the header printed when asking for an
authorization password;
@param description: is the generic description printed in the usage page;
@param help_specific_usage: is the specific parameter help
@param task_stop_fnc: is a function that will be called
whenever the task is stopped
@param task_submit_elaborate_specific_parameter_fnc: will be called passing
a key and a value, for parsing specific cli parameters. Must return True if
it has recognized the parameter. Must eventually update the options with
bibtask_set_option;
@param task_submit_check_options: must check the validity of options (via
bibtask_get_option) once all the options where parsed;
@param task_run_fnc: will be called as the main core function. Must return
False in case of errors.
"""
global _TASK_PARAMS, _OPTIONS
_TASK_PARAMS = {
"version" : version,
"task_stop_helper_fnc" : task_stop_helper_fnc,
"task_name" : os.path.basename(sys.argv[0]),
"task_specific_name" : '',
"user" : '',
"verbose" : 1,
"sleeptime" : '',
"runtime" : time.strftime("%Y-%m-%d %H:%M:%S"),
"priority" : 0,
"runtime_limit" : None,
"profile" : [],
}
to_be_submitted = True
if len(sys.argv) == 2 and sys.argv[1].isdigit():
_TASK_PARAMS['task_id'] = int(sys.argv[1])
argv = _task_get_options(_TASK_PARAMS['task_id'], _TASK_PARAMS['task_name'])
to_be_submitted = False
else:
argv = sys.argv
setup_loggers(_TASK_PARAMS.get('task_id'))
if type(argv) is dict:
# FIXME: REMOVE AFTER MAJOR RELEASE 1.0
# This is needed for old task submitted before CLI parameters
# where stored in DB and _OPTIONS dictionary was stored instead.
_OPTIONS = argv
else:
try:
_task_build_params(_TASK_PARAMS['task_name'], argv, description,
help_specific_usage, version, specific_params,
task_submit_elaborate_specific_parameter_fnc,
task_submit_check_options_fnc)
except SystemExit:
raise
except Exception, e:
register_exception(alert_admin=True)
write_message("Error in parsing the parameters: %s." % e, sys.stderr)
write_message("Exiting.", sys.stderr)
if not to_be_submitted:
task_update_status("ERROR")
raise
write_message('argv=%s' % (argv, ), verbose=9)
write_message('_OPTIONS=%s' % (_OPTIONS, ), verbose=9)
write_message('_TASK_PARAMS=%s' % (_TASK_PARAMS, ), verbose=9)
if to_be_submitted:
_task_submit(argv, authorization_action, authorization_msg)
else:
try:
if task_get_task_param('profile'):
try:
from cStringIO import StringIO
import pstats
filename = os.path.join(CFG_TMPDIR, 'bibsched_task_%s.pyprof' % _TASK_PARAMS['task_id'])
existing_sorts = pstats.Stats.sort_arg_dict_default.keys()
required_sorts = []
profile_dump = []
for sort in task_get_task_param('profile'):
if sort not in existing_sorts:
sort = 'cumulative'
if sort not in required_sorts:
required_sorts.append(sort)
if sys.hexversion < 0x02050000:
import hotshot
import hotshot.stats
pr = hotshot.Profile(filename)
ret = pr.runcall(_task_run, task_run_fnc)
for sort_type in required_sorts:
tmp_out = sys.stdout
sys.stdout = StringIO()
hotshot.stats.load(filename).strip_dirs().sort_stats(sort_type).print_stats()
# pylint: disable=E1103
# This is a hack. sys.stdout is a StringIO in this case.
profile_dump.append(sys.stdout.getvalue())
# pylint: enable=E1103
sys.stdout = tmp_out
else:
import cProfile
pr = cProfile.Profile()
ret = pr.runcall(_task_run, task_run_fnc)
pr.dump_stats(filename)
for sort_type in required_sorts:
strstream = StringIO()
pstats.Stats(filename, stream=strstream).strip_dirs().sort_stats(sort_type).print_stats()
profile_dump.append(strstream.getvalue())
profile_dump = '\n'.join(profile_dump)
profile_dump += '\nYou can use profile=%s' % existing_sorts
open(os.path.join(CFG_LOGDIR, 'bibsched_task_%d.log' % _TASK_PARAMS['task_id']), 'a').write("%s" % profile_dump)
os.remove(filename)
except ImportError:
ret = _task_run(task_run_fnc)
write_message("ERROR: The Python Profiler is not installed!", stream=sys.stderr)
else:
ret = _task_run(task_run_fnc)
if not ret:
write_message("Error occurred. Exiting.", sys.stderr)
except Exception, e:
register_exception(alert_admin=True)
write_message("Unexpected error occurred: %s." % e, sys.stderr)
write_message("Traceback is:", sys.stderr)
write_messages(''.join(traceback.format_tb(sys.exc_info()[2])), sys.stderr)
write_message("Exiting.", sys.stderr)
task_update_status("ERROR")
logging.shutdown()
def _task_build_params(
task_name,
argv,
description="",
help_specific_usage="",
version=__revision__,
specific_params=("", []),
task_submit_elaborate_specific_parameter_fnc=None,
task_submit_check_options_fnc=None):
""" Build the BibTask params.
@param argv: a list of string as in sys.argv
@param description: is the generic description printed in the usage page;
@param help_specific_usage: is the specific parameter help
@param task_submit_elaborate_specific_parameter_fnc: will be called passing
a key and a value, for parsing specific cli parameters. Must return True if
it has recognized the parameter. Must eventually update the options with
bibtask_set_option;
@param task_submit_check_options: must check the validity of options (via
bibtask_get_option) once all the options where parsed;
"""
global _OPTIONS
_OPTIONS = {}
if task_name in CFG_BIBTASK_DEFAULT_TASK_SETTINGS:
_OPTIONS.update(CFG_BIBTASK_DEFAULT_TASK_SETTINGS[task_name])
# set user-defined options:
try:
(short_params, long_params) = specific_params
opts, args = getopt.gnu_getopt(argv[1:], "hVv:u:s:t:P:N:L:" +
short_params, [
"help",
"version",
"verbose=",
"user=",
"sleep=",
"runtime=",
"priority=",
"name=",
"limit=",
"profile="
] + long_params)
except getopt.GetoptError, err:
_usage(1, err, help_specific_usage=help_specific_usage, description=description)
try:
for opt in opts:
if opt[0] in ("-h", "--help"):
_usage(0, help_specific_usage=help_specific_usage, description=description)
elif opt[0] in ("-V", "--version"):
print _TASK_PARAMS["version"]
sys.exit(0)
elif opt[0] in ("-u", "--user"):
_TASK_PARAMS["user"] = opt[1]
elif opt[0] in ("-v", "--verbose"):
_TASK_PARAMS["verbose"] = int(opt[1])
elif opt[0] in ("-s", "--sleeptime"):
if task_name not in CFG_TASK_IS_NOT_A_DEAMON:
get_datetime(opt[1]) # see if it is a valid shift
_TASK_PARAMS["sleeptime"] = opt[1]
elif opt[0] in ("-t", "--runtime"):
_TASK_PARAMS["runtime"] = get_datetime(opt[1])
elif opt[0] in ("-P", "--priority"):
_TASK_PARAMS["priority"] = int(opt[1])
elif opt[0] in ("-N", "--name"):
_TASK_PARAMS["task_specific_name"] = opt[1]
elif opt[0] in ("-L", "--limit"):
_TASK_PARAMS["runtime_limit"] = parse_runtime_limit(opt[1])
elif opt[0] in ("--profile", ):
_TASK_PARAMS["profile"] += opt[1].split(',')
elif not callable(task_submit_elaborate_specific_parameter_fnc) or \
not task_submit_elaborate_specific_parameter_fnc(opt[0],
opt[1], opts, args):
_usage(1, help_specific_usage=help_specific_usage, description=description)
except StandardError, e:
_usage(e, help_specific_usage=help_specific_usage, description=description)
if callable(task_submit_check_options_fnc):
if not task_submit_check_options_fnc():
_usage(1, help_specific_usage=help_specific_usage, description=description)
def task_set_option(key, value):
"""Set an value to key in the option dictionary of the task"""
global _OPTIONS
try:
_OPTIONS[key] = value
except NameError:
_OPTIONS = {key : value}
def task_get_option(key, default=None):
"""Returns the value corresponding to key in the option dictionary of the task"""
try:
return _OPTIONS.get(key, default)
except NameError:
return default
def task_has_option(key):
"""Map the has_key query to _OPTIONS"""
try:
return _OPTIONS.has_key(key)
except NameError:
return False
def task_get_task_param(key, default=None):
"""Returns the value corresponding to the particular task param"""
try:
return _TASK_PARAMS.get(key, default)
except NameError:
return default
def task_set_task_param(key, value):
"""Set the value corresponding to the particular task param"""
global _TASK_PARAMS
try:
_TASK_PARAMS[key] = value
except NameError:
_TASK_PARAMS = {key : value}
def task_update_progress(msg):
"""Updates progress information in the BibSched task table."""
write_message("Updating task progress to %s." % msg, verbose=9)
return run_sql("UPDATE schTASK SET progress=%s where id=%s",
(msg, _TASK_PARAMS["task_id"]))
def task_update_status(val):
"""Updates status information in the BibSched task table."""
write_message("Updating task status to %s." % val, verbose=9)
return run_sql("UPDATE schTASK SET status=%s where id=%s",
(val, _TASK_PARAMS["task_id"]))
def task_read_status():
"""Read status information in the BibSched task table."""
res = run_sql("SELECT status FROM schTASK where id=%s",
(_TASK_PARAMS['task_id'],), 1)
try:
out = res[0][0]
except:
out = 'UNKNOWN'
return out
def write_messages(msgs, stream=sys.stdout, verbose=1):
"""Write many messages through write_message"""
for msg in msgs.split('\n'):
write_message(msg, stream, verbose)
def write_message(msg, stream=sys.stdout, verbose=1):
"""Write message and flush output stream (may be sys.stdout or sys.stderr).
Useful for debugging stuff."""
if msg and _TASK_PARAMS['verbose'] >= verbose:
if stream == sys.stdout:
logging.info(msg)
elif stream == sys.stderr:
logging.error(msg)
else:
sys.stderr.write("Unknown stream %s. [must be sys.stdout or sys.stderr]\n" % stream)
else:
logging.debug(msg)
_RE_SHIFT = re.compile("([-\+]{0,1})([\d]+)([dhms])")
def get_datetime(var, format_string="%Y-%m-%d %H:%M:%S"):
"""Returns a date string according to the format string.
It can handle normal date strings and shifts with respect
to now."""
date = time.time()
factors = {"d":24*3600, "h":3600, "m":60, "s":1}
m = _RE_SHIFT.match(var)
if m:
sign = m.groups()[0] == "-" and -1 or 1
factor = factors[m.groups()[2]]
value = float(m.groups()[1])
date = time.localtime(date + sign * factor * value)
date = time.strftime(format_string, date)
else:
date = time.strptime(var, format_string)
date = time.strftime(format_string, date)
return date
_RE_RUNTIMELIMIT_FULL = re.compile(r"(?P[a-z]+)?\s*((?P\d\d?(:\d\d?)?)(-(?P\d\d?(:\d\d?)?))?)?", re.I)
_RE_RUNTIMELIMIT_HOUR = re.compile(r'(?P\d\d?)(:(?P\d\d?))?')
def parse_runtime_limit(value):
"""
Parsing CLI option for runtime limit, supplied as VALUE.
Value could be something like: Sunday 23:00-05:00, the format being
[Wee[kday]] [hh[:mm][-hh[:mm]]].
The function will return two valid time ranges. The first could be in the past, containing the present or in the future. The second is always in the future.
"""
def extract_time(value):
value = _RE_RUNTIMELIMIT_HOUR.search(value).groupdict()
hour = int(value['hour']) % 24
minutes = (value['minutes'] is not None and int(value['minutes']) or 0) % 60
return hour * 3600 + minutes * 60
def extract_weekday(value):
try:
return {
'mon' : 0,
'tue' : 1,
'wed' : 2,
'thu' : 3,
'fri' : 4,
'sat' : 5,
'sun' : 6,
}[value[:3].lower()]
except KeyError:
raise ValueError, "%s is not a good weekday name." % value
today = datetime.datetime.today()
try:
g = _RE_RUNTIMELIMIT_FULL.search(value)
if not g:
raise ValueError
pieces = g.groupdict()
today_weekday = today.isoweekday() - 1
if pieces['weekday'] is None:
## No weekday specified. So either today or tomorrow
first_occasion_day = 0
next_occasion_day = 24 * 3600
else:
## Weekday specified. So either this week or next
weekday = extract_weekday(pieces['weekday'])
first_occasion_day = -((today_weekday - weekday) % 7) * 24 * 3600
next_occasion_day = first_occasion_day + 7 * 24 * 3600
if pieces['begin'] is None:
pieces['begin'] = '00:00'
if pieces['end'] is None:
pieces['end'] = '00:00'
beginning_time = extract_time(pieces['begin'])
ending_time = extract_time(pieces['end'])
if beginning_time >= ending_time:
## end < begin we add a 24-hours watch tour.
ending_time += 24 * 3600
reference_time = time.mktime(datetime.datetime(today.year, today.month, today.day).timetuple())
first_range = (
reference_time + first_occasion_day + beginning_time,
reference_time + first_occasion_day + ending_time
)
second_range = (
reference_time + next_occasion_day + beginning_time,
reference_time + next_occasion_day + ending_time
)
return first_range, second_range
except ValueError:
raise
except:
raise ValueError, '"%s" does not seem to be correct format for parse_runtime_limit() [Wee[kday]] [hh[:mm][-hh[:mm]]]).' % value
def task_sleep_now_if_required(can_stop_too=False):
"""This function should be called during safe state of BibTask,
e.g. after flushing caches or outside of run_sql calls.
"""
status = task_read_status()
write_message('Entering task_sleep_now_if_required with status=%s' % status, verbose=9)
if status == 'ABOUT TO SLEEP':
write_message("sleeping...")
task_update_status("SLEEPING")
signal.signal(signal.SIGTSTP, _task_sig_dumb)
os.kill(os.getpid(), signal.SIGSTOP)
time.sleep(1)
task_update_status("CONTINUING")
write_message("... continuing...")
signal.signal(signal.SIGTSTP, _task_sig_sleep)
elif status == 'ABOUT TO STOP' and can_stop_too:
write_message("stopped")
task_update_status("STOPPED")
sys.exit(0)
runtime_limit = task_get_option("limit")
if runtime_limit is not None:
if not (runtime_limit[0] <= time.time() <= runtime_limit[1]):
if can_stop_too:
write_message("stopped (outside runtime limit)")
task_update_status("STOPPED")
sys.exit(0)
def authenticate(user, authorization_action, authorization_msg=""):
"""Authenticate the user against the user database.
Check for its password, if it exists.
Check for authorization_action access rights.
Return user name upon authorization success,
do system exit upon authorization failure.
"""
# With SSO it's impossible to check for pwd
if CFG_EXTERNAL_AUTH_USING_SSO or os.path.basename(sys.argv[0]) in CFG_VALID_PROCESSES_NO_AUTH_NEEDED:
return user
if authorization_msg:
print authorization_msg
print "=" * len(authorization_msg)
if user == "":
print >> sys.stdout, "\rUsername: ",
try:
user = sys.stdin.readline().lower().strip()
except EOFError:
sys.stderr.write("\n")
sys.exit(1)
except KeyboardInterrupt:
sys.stderr.write("\n")
sys.exit(1)
else:
print >> sys.stdout, "\rUsername:", user
## first check user:
# p_un passed may be an email or a nickname:
res = run_sql("select id from user where email=%s", (user,), 1) + \
run_sql("select id from user where nickname=%s", (user,), 1)
if not res:
print "Sorry, %s does not exist." % user
sys.exit(1)
else:
uid = res[0][0]
ok = False
login_method = get_user_preferences(uid)['login_method']
- if not CFG_EXTERNAL_AUTHENTICATION[login_method][0]:
+ if not CFG_EXTERNAL_AUTHENTICATION[login_method]:
#Local authentication, let's see if we want passwords.
res = run_sql("select id from user where id=%s "
"and password=AES_ENCRYPT(email,'')",
(uid,), 1)
if res:
ok = True
if not ok:
try:
password_entered = getpass.getpass()
except EOFError:
sys.stderr.write("\n")
sys.exit(1)
except KeyboardInterrupt:
sys.stderr.write("\n")
sys.exit(1)
- if not CFG_EXTERNAL_AUTHENTICATION[login_method][0]:
+ if not CFG_EXTERNAL_AUTHENTICATION[login_method]:
res = run_sql("select id from user where id=%s "
"and password=AES_ENCRYPT(email, %s)",
(uid, password_entered), 1)
if res:
ok = True
else:
- if CFG_EXTERNAL_AUTHENTICATION[login_method][0].auth_user(get_email(uid), password_entered):
+ if CFG_EXTERNAL_AUTHENTICATION[login_method].auth_user(get_email(uid), password_entered):
ok = True
if not ok:
print "Sorry, wrong credentials for %s." % user
sys.exit(1)
else:
## secondly check authorization for the authorization_action:
(auth_code, auth_message) = acc_authorize_action(uid, authorization_action)
if auth_code != 0:
print auth_message
sys.exit(1)
return user
def _task_submit(argv, authorization_action, authorization_msg):
"""Submits task to the BibSched task queue. This is what people will
be invoking via command line."""
## check as whom we want to submit?
check_running_process_user()
## sanity check: remove eventual "task" option:
## authenticate user:
_TASK_PARAMS['user'] = authenticate(_TASK_PARAMS["user"], authorization_action, authorization_msg)
## submit task:
if _TASK_PARAMS['task_specific_name']:
task_name = '%s:%s' % (_TASK_PARAMS['task_name'], _TASK_PARAMS['task_specific_name'])
else:
task_name = _TASK_PARAMS['task_name']
write_message("storing task options %s\n" % argv, verbose=9)
_TASK_PARAMS['task_id'] = run_sql("""INSERT INTO schTASK (proc,user,
runtime,sleeptime,status,progress,arguments,priority)
VALUES (%s,%s,%s,%s,'WAITING','',%s, %s)""",
(task_name, _TASK_PARAMS['user'], _TASK_PARAMS["runtime"],
_TASK_PARAMS["sleeptime"], marshal.dumps(argv), _TASK_PARAMS['priority']))
## update task number:
write_message("Task #%d submitted." % _TASK_PARAMS['task_id'])
return _TASK_PARAMS['task_id']
def _task_get_options(task_id, task_name):
"""Returns options for the task 'id' read from the BibSched task
queue table."""
out = {}
res = run_sql("SELECT arguments FROM schTASK WHERE id=%s AND proc LIKE %s",
(task_id, task_name+'%'))
try:
out = marshal.loads(res[0][0])
except:
write_message("Error: %s task %d does not seem to exist." \
% (task_name, task_id), sys.stderr)
task_update_status('ERROR')
sys.exit(1)
write_message('Options retrieved: %s' % (out, ), verbose=9)
return out
def _task_run(task_run_fnc):
"""Runs the task by fetching arguments from the BibSched task queue.
This is what BibSched will be invoking via daemon call.
The task prints Fibonacci numbers for up to NUM on the stdout, and some
messages on stderr.
@param task_run_fnc: will be called as the main core function. Must return
False in case of errors.
Return True in case of success and False in case of failure."""
## We prepare the pid file inside /prefix/var/run/taskname_id.pid
check_running_process_user()
try:
pidfile_name = os.path.join(CFG_PREFIX, 'var', 'run',
'bibsched_task_%d.pid' % _TASK_PARAMS['task_id'])
pidfile = open(pidfile_name, 'w')
pidfile.write(str(os.getpid()))
pidfile.close()
except OSError:
register_exception(alert_admin=True)
task_update_status("ERROR")
return False
## check task status:
task_status = task_read_status()
if task_status not in ("WAITING", "SCHEDULED"):
write_message("Error: The task #%d is %s. I expected WAITING or SCHEDULED." %
(_TASK_PARAMS['task_id'], task_status), sys.stderr)
return False
time_now = time.time()
if _TASK_PARAMS['runtime_limit'] is not None and os.environ.get('BIBSCHED_MODE', 'manual') != 'manual':
if not _TASK_PARAMS['runtime_limit'][0][0] <= time_now <= _TASK_PARAMS['runtime_limit'][0][1]:
if time_now <= _TASK_PARAMS['runtime_limit'][0][0]:
new_runtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(_TASK_PARAMS['runtime_limit'][0][0]))
else:
new_runtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(_TASK_PARAMS['runtime_limit'][1][0]))
progress = run_sql("SELECT progress FROM schTASK WHERE id=%s", (_TASK_PARAMS['task_id'], ))
if progress:
progress = progress[0][0]
else:
progress = ''
g = re.match(r'Postponed (\d+) time\(s\)', progress)
if g:
postponed_times = int(g.group(1))
else:
postponed_times = 0
run_sql("UPDATE schTASK SET runtime=%s, status='WAITING', progress=%s WHERE id=%s", (new_runtime, 'Postponed %d time(s)' % (postponed_times + 1), _TASK_PARAMS['task_id']))
write_message("Task #%d postponed because outside of runtime limit" % _TASK_PARAMS['task_id'])
return True
## initialize signal handler:
signal.signal(signal.SIGUSR2, signal.SIG_IGN)
signal.signal(signal.SIGTSTP, _task_sig_sleep)
signal.signal(signal.SIGTERM, _task_sig_stop)
signal.signal(signal.SIGQUIT, _task_sig_stop)
signal.signal(signal.SIGABRT, _task_sig_suicide)
signal.signal(signal.SIGINT, _task_sig_stop)
## we can run the task now:
write_message("Task #%d started." % _TASK_PARAMS['task_id'])
task_update_status("RUNNING")
## run the task:
_TASK_PARAMS['task_starting_time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
sleeptime = _TASK_PARAMS['sleeptime']
try:
try:
if callable(task_run_fnc) and task_run_fnc():
task_update_status("DONE")
else:
task_update_status("DONE WITH ERRORS")
except SystemExit:
pass
except:
register_exception(alert_admin=True)
task_update_status("ERROR")
finally:
task_status = task_read_status()
if sleeptime:
new_runtime = get_datetime(sleeptime)
## The task is a daemon. We resubmit it
if task_status == 'DONE':
## It has finished in a good way. We recycle the database row
run_sql("UPDATE schTASK SET runtime=%s, status='WAITING', progress='' WHERE id=%s", (new_runtime, _TASK_PARAMS['task_id']))
write_message("Task #%d finished and resubmitted." % _TASK_PARAMS['task_id'])
elif task_status == 'STOPPED':
run_sql("UPDATE schTASK SET status='WAITING', progress='' WHERE id=%s", (_TASK_PARAMS['task_id'], ))
write_message("Task #%d stopped and resubmitted." % _TASK_PARAMS['task_id'])
else:
## We keep the bad result and we resubmit with another id.
#res = run_sql('SELECT proc,user,sleeptime,arguments,priority FROM schTASK WHERE id=%s', (_TASK_PARAMS['task_id'], ))
#proc, user, sleeptime, arguments, priority = res[0]
#run_sql("""INSERT INTO schTASK (proc,user,
#runtime,sleeptime,status,arguments,priority)
#VALUES (%s,%s,%s,%s,'WAITING',%s, %s)""",
#(proc, user, new_runtime, sleeptime, arguments, priority))
write_message("Task #%d finished but not resubmitted. [%s]" % (_TASK_PARAMS['task_id'], task_status))
else:
## we are done:
write_message("Task #%d finished. [%s]" % (_TASK_PARAMS['task_id'], task_status))
## Removing the pid
os.remove(pidfile_name)
return True
def _usage(exitcode=1, msg="", help_specific_usage="", description=""):
"""Prints usage info."""
if msg:
sys.stderr.write("Error: %s.\n" % msg)
sys.stderr.write("Usage: %s [options]\n" % sys.argv[0])
if help_specific_usage:
sys.stderr.write("Command options:\n")
sys.stderr.write(help_specific_usage)
sys.stderr.write("Scheduling options:\n")
sys.stderr.write(" -u, --user=USER\tUser name under which to submit this"
" task.\n")
sys.stderr.write(" -t, --runtime=TIME\tTime to execute the task. [default=now]\n"
"\t\t\tExamples: +15s, 5m, 3h, 2002-10-27 13:57:26.\n")
sys.stderr.write(" -s, --sleeptime=SLEEP\tSleeping frequency after"
" which to repeat the task.\n"
"\t\t\tExamples: 30m, 2h, 1d. [default=no]\n")
sys.stderr.write(" -L --limit=LIMIT\tTime limit when it is"
" allowed to execute the task.\n"
"\t\t\tExamples: 22:00-03:00, Sunday 01:00-05:00.\n"
"\t\t\tSyntax: [Wee[kday]] [hh[:mm][-hh[:mm]]].\n")
sys.stderr.write(" -P, --priority=PRI\tTask priority (0=default, 1=higher, etc).\n")
sys.stderr.write(" -N, --name=NAME\tTask specific name (advanced option).\n")
sys.stderr.write("General options:\n")
sys.stderr.write(" -h, --help\t\tPrint this help.\n")
sys.stderr.write(" -V, --version\t\tPrint version information.\n")
sys.stderr.write(" -v, --verbose=LEVEL\tVerbose level (0=min,"
" 1=default, 9=max).\n")
sys.stderr.write(" --profile=STATS\tPrint profile information. STATS is a comma-separated\n\t\t\tlist of desired output stats (calls, cumulative,\n\t\t\tfile, line, module, name, nfl, pcalls, stdname, time).\n")
if description:
sys.stderr.write(description)
sys.exit(exitcode)
def _task_sig_sleep(sig, frame):
"""Signal handler for the 'sleep' signal sent by BibSched."""
signal.signal(signal.SIGTSTP, signal.SIG_IGN)
write_message("task_sig_sleep(), got signal %s frame %s"
% (sig, frame), verbose=9)
write_message("sleeping as soon as possible...")
_db_login(1)
task_update_status("ABOUT TO SLEEP")
def _task_sig_stop(sig, frame):
"""Signal handler for the 'stop' signal sent by BibSched."""
write_message("task_sig_stop(), got signal %s frame %s"
% (sig, frame), verbose=9)
write_message("stopping as soon as possible...")
_db_login(1) # To avoid concurrency with an interrupted run_sql call
task_update_status("ABOUT TO STOP")
def _task_sig_suicide(sig, frame):
"""Signal handler for the 'suicide' signal sent by BibSched."""
write_message("task_sig_suicide(), got signal %s frame %s"
% (sig, frame), verbose=9)
write_message("suiciding myself now...")
task_update_status("SUICIDING")
write_message("suicided")
_db_login(1)
task_update_status("SUICIDED")
sys.exit(1)
def _task_sig_dumb(sig, frame):
"""Dumb signal handler."""
pass
_RE_PSLINE = re.compile('^\s*(.+?)\s+(.+?)\s*$')
def guess_apache_process_user_from_ps():
"""Guess Apache process user by parsing the list of running processes."""
apache_users = []
try:
# Tested on Linux, Sun and MacOS X
for line in os.popen('ps -A -o user,comm').readlines():
g = _RE_PSLINE.match(line)
if g:
username = g.group(2)
process = os.path.basename(g.group(1))
if process in ('apache', 'apache2', 'httpd') :
if username not in apache_users and username != 'root':
apache_users.append(username)
except Exception, e:
print >> sys.stderr, "WARNING: %s" % e
return tuple(apache_users)
def guess_apache_process_user():
"""
Return the possible name of the user running the Apache server process.
(Look at running OS processes or look at OS users defined in /etc/passwd.)
"""
apache_users = guess_apache_process_user_from_ps() + ('apache2', 'apache', 'www-data')
for username in apache_users:
try:
userline = pwd.getpwnam(username)
return userline[0]
except KeyError:
pass
print >> sys.stderr, "ERROR: Cannot detect Apache server process user. Please set the correct value in CFG_BIBSCHED_PROCESS_USER."
sys.exit(1)
def check_running_process_user():
"""
Check that the user running this program is the same as the user
configured in CFG_BIBSCHED_PROCESS_USER or as the user running the
Apache webserver process.
"""
running_as_user = pwd.getpwuid(os.getuid())[0]
if CFG_BIBSCHED_PROCESS_USER:
# We have the expected bibsched process user defined in config,
# so check against her, not against Apache.
if running_as_user != CFG_BIBSCHED_PROCESS_USER:
print >> sys.stderr, """ERROR: You must run "%(x_proc)s" as the user set up in your
CFG_BIBSCHED_PROCESS_USER (seems to be "%(x_user)s").
You may want to do "sudo -u %(x_user)s %(x_proc)s ..." to do so.
If you think this is not right, please set CFG_BIBSCHED_PROCESS_USER
appropriately and rerun "inveniocfg --update-config-py".""" % \
{'x_proc': os.path.basename(sys.argv[0]), 'x_user': CFG_BIBSCHED_PROCESS_USER}
sys.exit(1)
elif running_as_user != guess_apache_process_user(): # not defined in config, check against Apache
print >> sys.stderr, """ERROR: You must run "%(x_proc)s" as the same user that runs your Apache server
process (seems to be "%(x_user)s").
You may want to do "sudo -u %(x_user)s %(x_proc)s ..." to do so.
If you think this is not right, please set CFG_BIBSCHED_PROCESS_USER
appropriately and rerun "inveniocfg --update-config-py".""" % \
{'x_proc': os.path.basename(sys.argv[0]), 'x_user': guess_apache_process_user()}
sys.exit(1)
return
diff --git a/modules/miscutil/demo/Makefile.am b/modules/miscutil/demo/Makefile.am
index 3f9239a92..e54f5d92c 100644
--- a/modules/miscutil/demo/Makefile.am
+++ b/modules/miscutil/demo/Makefile.am
@@ -1,28 +1,26 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
sqldir = $(libdir)/sql/invenio
sql_DATA = democfgdata.sql
tmpdir = $(prefix)/var/tmp
-tmp_DATA = demobibdata.xml \
- demo-site-apache-user-passwords \
- demo-site-apache-user-groups
+tmp_DATA = demobibdata.xml
EXTRA_DIST = $(sql_DATA) $(tmp_DATA)
CLEANFILES = *~ *.tmp
diff --git a/modules/miscutil/demo/demo-site-apache-user-groups b/modules/miscutil/demo/demo-site-apache-user-groups
deleted file mode 100644
index 4e67bfc2f..000000000
--- a/modules/miscutil/demo/demo-site-apache-user-groups
+++ /dev/null
@@ -1 +0,0 @@
-theses: jekyll
\ No newline at end of file
diff --git a/modules/miscutil/demo/demo-site-apache-user-passwords b/modules/miscutil/demo/demo-site-apache-user-passwords
deleted file mode 100644
index 9731dd30d..000000000
--- a/modules/miscutil/demo/demo-site-apache-user-passwords
+++ /dev/null
@@ -1,2 +0,0 @@
-jekyll:sQcN.VNjOmFLo
-hyde:j/QjLyXt8iWYs
\ No newline at end of file
diff --git a/modules/miscutil/lib/errorlib.py b/modules/miscutil/lib/errorlib.py
index 06cec64ac..126d2bf7d 100644
--- a/modules/miscutil/lib/errorlib.py
+++ b/modules/miscutil/lib/errorlib.py
@@ -1,639 +1,644 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Error handling library """
__revision__ = "$Id$"
import traceback
import os
import sys
import time
import re
from cStringIO import StringIO
from invenio.config import CFG_SITE_LANG, CFG_LOGDIR, \
CFG_WEBALERT_ALERT_ENGINE_EMAIL, CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_SUPPORT_EMAIL, CFG_SITE_NAME, CFG_SITE_URL, CFG_VERSION, \
CFG_CERN_SITE, CFG_SITE_EMERGENCY_PHONE_NUMBERS, \
CFG_SITE_ADMIN_EMAIL_EXCEPTIONS
from invenio.miscutil_config import CFG_MISCUTIL_ERROR_MESSAGES
from invenio.urlutils import wash_url_argument
from invenio.messages import wash_language, gettext_set_language
from invenio.dateutils import convert_datestruct_to_datetext
## Regular expression to match possible password related variable that should
## be disclosed in frame analysis.
RE_PWD = re.compile(r"pwd|pass", re.I)
def get_client_info(req):
"""
Returns a dictionary with client information
@param req: mod_python request
"""
try:
return {
'host': req.hostname,
'url': req.unparsed_uri,
'time': convert_datestruct_to_datetext(time.localtime()),
'browser': 'User-Agent' in req.headers_in and \
req.headers_in['User-Agent'] or "N/A",
'client_ip': req.remote_ip}
except:
return {}
def get_pretty_wide_client_info(req):
"""Return in a pretty way all the avilable information about the current
user/client"""
if req:
from invenio.webuser import collect_user_info
user_info = collect_user_info(req)
keys = user_info.keys()
keys.sort()
max_key = max([len(key) for key in keys])
ret = ""
fmt = "%% %is: %%s\n" % max_key
for key in keys:
if key in ('uri', 'referer'):
ret += fmt % (key, "<%s>" % user_info[key])
else:
ret += fmt % (key, user_info[key])
if ret.endswith('\n'):
return ret[:-1]
else:
return ret
else:
return "No client information available"
def get_tracestack():
"""
If an exception has been caught, return the system tracestack or else
return tracestack of what is currently in the stack
"""
if traceback.format_tb(sys.exc_info()[2]):
delimiter = "\n"
tracestack_pretty = "Traceback: \n%s" % \
delimiter.join(traceback.format_tb(sys.exc_info()[2]))
else:
## force traceback except for this call
tracestack = traceback.extract_stack()[:-1]
tracestack_pretty = "%sForced traceback (most recent call last)" % \
(' '*4, )
for trace_tuple in tracestack:
tracestack_pretty += """
File "%(file)s", line %(line)s, in %(function)s
%(text)s""" % {
'file': trace_tuple[0],
'line': trace_tuple[1],
'function': trace_tuple[2],
'text': trace_tuple[3] is not None and \
str(trace_tuple[3]) or ""}
return tracestack_pretty
def send_sms(phone_number, msg):
"""Send msg as an SMS to each phone number.
Note: this function is just an example and works only at CERN
it should be reimplemented in your own instituition.
"""
if not CFG_CERN_SITE:
raise NotImplementedError("Implement this function "
"with your own method")
if phone_number[0] == '+':
phone_number = '00' + phone_number[1:]
if phone_number[0] != '0':
phone_number = '00' + phone_number
from invenio.mailutils import send_email
return send_email(CFG_SITE_SUPPORT_EMAIL,
phone_number + '@mail2sms.cern.ch', '', msg, header='', footer='')
def register_emergency(msg, send_sms_function=send_sms):
"""Launch an emergency. This means to send sms messages to each
phone number in CFG_SITE_EMERGENCY_PHONE_NUMBERS."""
for phone_number in CFG_SITE_EMERGENCY_PHONE_NUMBERS:
send_sms_function(phone_number, msg)
def find_all_values_to_hide(local_variables, analyzed_stack=None):
"""Return all the potential password to hyde."""
## Let's add at least the DB password.
if analyzed_stack is None:
from invenio.dbquery import CFG_DATABASE_PASS
ret = set([CFG_DATABASE_PASS])
analyzed_stack = set()
else:
ret = set()
for key, value in local_variables.iteritems():
if id(value) in analyzed_stack:
## Let's avoid loops
continue
analyzed_stack.add(id(value))
if RE_PWD.search(key):
ret.add(str(value))
if isinstance(value, dict):
ret |= find_all_values_to_hide(value, analyzed_stack)
+ if '' in ret:
+ ## Let's discard the empty string in case there is an empty password,
+ ## or otherwise anything will be separated by '<*****>' in the output
+ ## :-)
+ ret.remove('')
return ret
def get_pretty_traceback(req=None, exc_info=None, force_stack=True):
"""
Given an optional request object and an optional exc_info,
returns a text string representing many details about an exception.
"""
if exc_info is None:
exc_info = sys.exc_info()
if exc_info[0]:
## We found an exception.
## We want to extract the name of the Exception
exc_name = exc_info[0].__name__
exc_value = str(exc_info[1])
## Let's record when and where and what
www_data = "%(time)s -> %(name)s: %(value)s" % {
'time': time.strftime("%Y-%m-%d %H:%M:%S"),
'name': exc_name,
'value': exc_value}
## Let's retrieve contextual user related info, if any
try:
client_data = get_pretty_wide_client_info(req)
except Exception, err:
client_data = "Error in retrieving " \
"contextual information: %s" % err
## Let's extract the traceback:
if not exc_name.startswith('Invenio') or force_stack:
tracestack_data_stream = StringIO()
tb = sys.exc_info()[2]
while 1:
if not tb.tb_next:
break
tb = tb.tb_next
stack = []
f = tb.tb_frame
while f:
stack.append(f)
f = f.f_back
stack.reverse()
stack = stack[-10:] ## Let's just take the last few frames
traceback.print_exc(file=tracestack_data_stream)
print >> tracestack_data_stream, \
"Locals by frame, innermost last"
values_to_hide = set()
for frame in stack:
print >> tracestack_data_stream
print >> tracestack_data_stream, \
">>>> Frame %s in %s at line %s" % (
frame.f_code.co_name,
frame.f_code.co_filename,
frame.f_lineno)
try:
values_to_hide |= find_all_values_to_hide(frame.f_locals)
code = open(frame.f_code.co_filename).readlines()
first_line = max(1, frame.f_lineno-3)
last_line = min(len(code), frame.f_lineno+3)
print >> tracestack_data_stream, "*" * 79
for line in xrange(first_line, last_line+1):
code_line = code[line-1].rstrip()
for value in values_to_hide:
## Let's hide passwords
code_line = code_line.replace(value, '<*****>')
if line == frame.f_lineno:
print >> tracestack_data_stream, \
"----> %4i %s" % (line, code_line)
else:
print >> tracestack_data_stream, \
" %4i %s" % (line, code_line)
print >> tracestack_data_stream, "*" * 79
except:
pass
for key, value in frame.f_locals.items():
print >> tracestack_data_stream, "\t%20s = " % key,
for to_hide in values_to_hide:
## Let's hide passwords
value = repr(value).replace(to_hide, '<*****>')
try:
print >> tracestack_data_stream, \
_truncate_dynamic_string(value)
except:
print >> tracestack_data_stream, \
""
tracestack_data = tracestack_data_stream.getvalue()
else:
tracestack_data = ''
## Okay, start printing:
output = StringIO()
print >> output, '\n',
print >> output, "The following problem occurred on <%s>" \
" (Invenio %s)" % (CFG_SITE_URL, CFG_VERSION)
print >> output, "\n>> %s" % www_data
print >> output, "\n>>> User details\n"
print >> output, client_data
if tracestack_data:
print >> output, "\n>>> Traceback details\n"
print >> output, tracestack_data
return output.getvalue()
else:
return ""
def register_exception(force_stack=False,
stream='error',
req=None,
prefix='',
suffix='',
alert_admin=False,
subject=''):
"""
Log error exception to invenio.err and warning exception to invenio.log.
Errors will be logged together with client information (if req is
given).
Note: For sanity reasons, dynamic params such as PREFIX, SUFFIX and
local stack variables are checked for length, and only first 500
chars of their values are printed.
@param force_stack: when True stack is always printed, while when False,
stack is printed only whenever the Exception type is not containing the
word Invenio
@param stream: 'error' or 'warning'
@param req: mod_python request
@param prefix: a message to be printed before the exception in
the log
@param suffix: a message to be printed before the exception in
the log
@param alert_admin: wethever to send the exception to the administrator via
email. Note this parameter is bypassed when
CFG_SITE_ADMIN_EMAIL_EXCEPTIONS is set to a value different than 1
@param subject: overrides the email subject
@return: 1 if successfully wrote to stream, 0 if not
"""
try:
## Let's extract exception information
exc_info = sys.exc_info()
output = get_pretty_traceback(
req=req, exc_info=exc_info, force_stack=force_stack)
if output:
## Okay, start printing:
log_stream = StringIO()
email_stream = StringIO()
print >> email_stream, '\n',
## If a prefix was requested let's print it
if prefix:
prefix = _truncate_dynamic_string(prefix)
print >> log_stream, prefix + '\n'
print >> email_stream, prefix + '\n'
print >> log_stream, output
print >> email_stream, output
## If a suffix was requested let's print it
if suffix:
suffix = _truncate_dynamic_string(suffix)
print >> log_stream, suffix
print >> email_stream, suffix
log_text = log_stream.getvalue()
email_text = email_stream.getvalue()
if email_text.endswith('\n'):
email_text = email_text[:-1]
## Preparing the exception dump
stream = stream=='error' and 'err' or 'log'
## We now have the whole trace
written_to_log = False
try:
## Let's try to write into the log.
open(os.path.join(CFG_LOGDIR, 'invenio.' + stream), 'a').write(
log_text)
written_to_log = True
except:
written_to_log = False
if CFG_SITE_ADMIN_EMAIL_EXCEPTIONS > 1 or \
(alert_admin and CFG_SITE_ADMIN_EMAIL_EXCEPTIONS > 0) or \
not written_to_log:
## If requested or if it's impossible to write in the log
from invenio.mailutils import send_email
if not subject:
filename, line_no, function_name = _get_filename_and_line(exc_info)
subject = 'Exception (%s:%s:%s)' % (filename, line_no, function_name)
subject = '%s at %s' % (subject, CFG_SITE_URL)
if not written_to_log:
email_text += """\
Note that this email was sent to you because it has been impossible to log
this exception into %s""" % os.path.join(CFG_LOGDIR, 'invenio.' + stream)
send_email(
CFG_SITE_ADMIN_EMAIL,
CFG_SITE_ADMIN_EMAIL,
subject=subject,
content=email_text)
return 1
else:
return 0
except Exception, err:
print >> sys.stderr, "Error in registering exception to '%s': '%s'" % (
CFG_LOGDIR + '/invenio.' + stream, err)
return 0
def register_errors(errors_or_warnings_list, stream, req=None):
"""
log errors to invenio.err and warnings to invenio.log
errors will be logged with client information (if req is given) and a
tracestack warnings will be logged with just the warning message
@param errors_or_warnings_list: list of tuples (err_name, err_msg)
err_name = ERR_ + %(module_directory_name)s + _ + %(error_name)s #ALL CAPS
err_name must be stored in file: module_directory_name + _config.py
as the key for dict with name: CFG_ + %(module_directory_name)s +
_ERROR_MESSAGES
@param stream: 'error' or 'warning'
@param req: mod_python request
@return: tuple integer 1 if successfully wrote to stream, integer 0 if not
will append another error to errors_list if unsuccessful
"""
client_info_dict = ""
if stream == "error":
# call the stack trace now
tracestack_pretty = get_tracestack()
# if req is given, get client info
if req:
client_info_dict = get_client_info(req)
if client_info_dict:
client_info = \
'''URL: http://%(host)s%(url)s
Browser: %(browser)s
Client: %(client_ip)s''' % client_info_dict
else:
client_info = "No client information available"
else:
client_info = "No client information available"
# check arguments
errors_or_warnings_list = wash_url_argument(
errors_or_warnings_list, 'list')
stream = wash_url_argument(stream, 'str')
for etuple in errors_or_warnings_list:
etuple = wash_url_argument(etuple, 'tuple')
# check stream arg for presence of [error,warning]; when none, add error
# and default to warning
if stream == 'error':
stream = 'err'
elif stream == 'warning':
stream = 'log'
else:
stream = 'log'
error = 'ERR_MISCUTIL_BAD_FILE_ARGUMENT_PASSED'
errors_or_warnings_list.append(
(error, eval(CFG_MISCUTIL_ERROR_MESSAGES[error])% stream))
# update log_errors
stream_location = os.path.join(CFG_LOGDIR, 'invenio.' + stream)
errors = ''
for etuple in errors_or_warnings_list:
try:
errors += "%s%s : %s \n " % (' '*4*7+' ', etuple[0], etuple[1])
except:
errors += "%s%s \n " % (' '*4*7+' ', etuple)
if errors:
errors = errors[(4*7+1):-3] # get rid of begining spaces and last '\n'
msg = """
%(time)s --> %(errors)s%(error_file)s""" % {
'time': client_info_dict and client_info_dict['time'] or \
time.strftime("%Y-%m-%d %H:%M:%S"),
'errors': errors,
'error_file': stream=='err' and "\n%s%s\n%s\n" % (
' '*4, client_info, tracestack_pretty) or ""}
try:
stream_to_write = open(stream_location, 'a+')
stream_to_write.writelines(msg)
stream_to_write.close()
return_value = 1
except:
error = 'ERR_MISCUTIL_WRITE_FAILED'
errors_or_warnings_list.append(
(error, CFG_MISCUTIL_ERROR_MESSAGES[error] % stream_location))
return_value = 0
return return_value
def get_msg_associated_to_code(err_code, stream='error'):
"""
Returns string of code
@param err_code: error or warning code
@type err_code: string
@param stream: 'error' or 'warning'
@type stream: string
@return: (err_code, formatted_message)
@rtype: tuple
"""
err_code = wash_url_argument(err_code, 'str')
stream = wash_url_argument(stream, 'str')
try:
module_directory_name = err_code.split('_')[1].lower()
module_config = module_directory_name + '_config'
module_dict_name = "CFG_" + module_directory_name.upper() + \
"_%s_MESSAGES" % stream.upper()
module = __import__(
module_config, globals(), locals(), [module_dict_name])
module_dict = getattr(module, module_dict_name)
err_msg = module_dict[err_code]
except ImportError:
error = 'ERR_MISCUTIL_IMPORT_ERROR'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % (err_code,
module_config)
err_code = error
except AttributeError:
error = 'ERR_MISCUTIL_NO_DICT'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % (err_code,
module_config,
module_dict_name)
err_code = error
except KeyError:
error = 'ERR_MISCUTIL_NO_MESSAGE_IN_DICT'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % (err_code,
module_config + '.' + module_dict_name)
err_code = error
except:
error = 'ERR_MISCUTIL_UNDEFINED_ERROR'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % err_code
err_code = error
return (err_code, err_msg)
def get_msgs_for_code_list(code_list, stream='error', ln=CFG_SITE_LANG):
"""
@param code_list: list of tuples [(err_name, arg1, ..., argN), ...]
err_name = ERR_ + %(module_directory_name)s + _ + %(error_name)s #ALL CAPS
err_name must be stored in file: module_directory_name + _config.py
as the key for dict with name: CFG_ + %(module_directory_name)s +
_ERROR_MESSAGES
For warnings, same thing except:
err_name can begin with either 'ERR' or 'WRN'
dict name ends with _warning_messages
@param stream: 'error' or 'warning'
@return: list of tuples of length 2 [('ERR_...', err_msg), ...]
if code_list empty, will return None.
if errors retrieving error messages, will append an error to the list
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
out = []
if type(code_list) is None:
return None
code_list = wash_url_argument(code_list, 'list')
stream = wash_url_argument(stream, 'str')
for code_tuple in code_list:
if not(type(code_tuple) is tuple):
code_tuple = (code_tuple, )
nb_tuple_args = len(code_tuple) - 1
err_code = code_tuple[0]
if stream == 'error' and not err_code.startswith('ERR'):
error = 'ERR_MISCUTIL_NO_ERROR_MESSAGE'
out.append((error, eval(CFG_MISCUTIL_ERROR_MESSAGES[error])))
continue
elif stream == 'warning' and not (err_code.startswith('ERR') or \
err_code.startswith('WRN')):
error = 'ERR_MISCUTIL_NO_WARNING_MESSAGE'
out.append((error, eval(CFG_MISCUTIL_ERROR_MESSAGES[error])))
continue
(new_err_code, err_msg) = get_msg_associated_to_code(err_code, stream)
if err_msg[:2] == '_(' and err_msg[-1] == ')':
# err_msg is internationalized
err_msg = eval(err_msg)
nb_msg_args = err_msg.count('%') - err_msg.count('%%')
parsing_error = ""
if new_err_code != err_code or nb_msg_args == 0:
# undefined_error or immediately displayable error
out.append((new_err_code, err_msg))
continue
try:
if nb_msg_args == nb_tuple_args:
err_msg = err_msg % code_tuple[1:]
elif nb_msg_args < nb_tuple_args:
err_msg = err_msg % code_tuple[1:nb_msg_args+1]
parsing_error = 'ERR_MISCUTIL_TOO_MANY_ARGUMENT'
parsing_error_message = eval(
CFG_MISCUTIL_ERROR_MESSAGES[parsing_error])
parsing_error_message %= code_tuple[0]
elif nb_msg_args > nb_tuple_args:
code_tuple = list(code_tuple)
for dummy in range(nb_msg_args - nb_tuple_args):
code_tuple.append('???')
code_tuple = tuple(code_tuple)
err_msg = err_msg % code_tuple[1:]
parsing_error = 'ERR_MISCUTIL_TOO_FEW_ARGUMENT'
parsing_error_message = eval(
CFG_MISCUTIL_ERROR_MESSAGES[parsing_error])
parsing_error_message %= code_tuple[0]
except:
parsing_error = 'ERR_MISCUTIL_BAD_ARGUMENT_TYPE'
parsing_error_message = eval(
CFG_MISCUTIL_ERROR_MESSAGES[parsing_error])
parsing_error_message %= code_tuple[0]
out.append((err_code, err_msg))
if parsing_error:
out.append((parsing_error, parsing_error_message))
if not(out):
out = None
return out
def send_error_report_to_admin(header, url, time_msg,
browser, client, error,
sys_error, traceback_msg):
"""
Sends an email to the admin with client info and tracestack
"""
from_addr = '%s Alert Engine <%s>' % (
CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
to_addr = CFG_SITE_ADMIN_EMAIL
body = """
The following error was seen by a user and sent to you.
%(contact)s
%(header)s
%(url)s
%(time)s
%(browser)s
%(client)s
%(error)s
%(sys_error)s
%(traceback)s
Please see the %(logdir)s/invenio.err for traceback details.""" % {
'header': header,
'url': url,
'time': time_msg,
'browser': browser,
'client': client,
'error': error,
'sys_error': sys_error,
'traceback': traceback_msg,
'logdir': CFG_LOGDIR,
'contact': "Please contact %s quoting the following information:" %
(CFG_SITE_SUPPORT_EMAIL, )}
from invenio.mailutils import send_email
send_email(from_addr, to_addr, subject="Error notification", content=body)
def _get_filename_and_line(exc_info):
"""
Return the filename, the line and the function_name where the exception happened.
"""
tb = exc_info[2]
exception_info = traceback.extract_tb(tb)[-1]
filename = os.path.basename(exception_info[0])
line_no = exception_info[1]
function_name = exception_info[2]
return filename, line_no, function_name
def _truncate_dynamic_string(val, maxlength=500):
"""
Return at most MAXLENGTH characters of VAL. Useful for
sanitizing dynamic variable values in the output.
"""
out = repr(val)
if len(out) > maxlength:
out = out[:maxlength] + ' [...]'
return out
diff --git a/modules/webaccess/doc/admin/webaccess-admin-guide.webdoc b/modules/webaccess/doc/admin/webaccess-admin-guide.webdoc
index 9a84e4be8..c72418353 100644
--- a/modules/webaccess/doc/admin/webaccess-admin-guide.webdoc
+++ b/modules/webaccess/doc/admin/webaccess-admin-guide.webdoc
@@ -1,1145 +1,1136 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
WebAccess is a common RBAC, role based access control, for all of
Invenio. This means that users are connected to roles that cover
different areas of access. I.e administrator of the photo
collection or system librarian. Users can be active in
different areas and of course connected to as many roles as needed.
The roles are connected to actions. An action identifies a task you
can perform in Invenio. It can be defined to take any number of
arguments in order to more clearly describe what you are allowing
connected users to do.
For example the system librarian can be allowed to run bibindex on
the different indexes. To allow system librarians to run the
bibindex indexing on the field author we connect role system
librarian with action runbibindex using the argument
index='author'.
Additionally, roles could have firewall-like role
definitions. A definition is a formal description of which
users are entitled to belong to the role. So you have two ways for
connecting users to roles. Either linking explicitly a user with the
role or describing the characteristics that makes users belong to
the role.
WebAccess is based on allowing users to perform actions. This means
that only allowed actions are stored in the access control engine's
database.
All the WebAccess Administration web pages have certain
features/design choices in common
- Divided into steps
The process of adding new authorizations/information is
stepwise. The subtitle contains information about wich step you are
on and what you are supposed to do.
- Restart from any wanted step
You can always start from an earlier step by simply clicking the
wanted button. This is not a way to undo changes! No information
about previous database is kept, so all changes are definite.
- Change or new entry must confirmed
On all the pages you will be asked to confirm the change, with
information about what kind of change you are about to perform.
- Links to other relevant admin areas on the right side
To make it easier to perform your administration tasks, we have
added a menu area on the right hand side of these pages. The menu
contain links to other relevant admin pages and change according to
the page you are on and the information you have selected.
I. Role area
II. Example - connecting role and user
I. Role area
Administration tasks starts in one of the administration areas. The
role area is the main area from where you can perform all your
managing tasks. The other admin areas are just other ways of
entering.
II. Example - connecting role and user
One of the important tasks that can be handled via the WebAccess Admin Web Interface
is the delegation of access rights to users. This is done by connecting them to the
different roles offered.
The task is divided into 5 simple and comprehensive steps. Below follows the pages from
the different steps with comments on the ongoing procedure.
- step 1 - select a role
You must first select the role you want to connect users to. All the available roles are
listed alfabetically in a select box. Just find the wanted role and select it. Then click on
the button saying "select role".
If you start from the Role Area, this step is already done, and you start directly on step 2.
- step 2 - search for users
As you can see, the subtitle of the page has now changed. The subtitle always tells you
which step you are on and what your current task is.
There can be possibly thousands of users using your online library, therefore it is important
to make it easier to identify the user you are looking for. Give part of, or the entire search
string and all users with partly matching e-mails will be listed on the next step.
You can also see that the right hand menu has changed. This area is always updated with links
to related admin areas.
start adding new authorizations to role superadmin.
- step 3 - select a user.
The select box contains all users with partly matching e-mail addresses. Select the one
you want to connect to the role and continue.
Notice the navigation trail that tells you were on the Administrator pages you are currently
working.
start adding new authorizations to role superadmin.
- step 4 - confirm to add user
All WebAccess Administrator web pages display the action you are about to peform, this
means explaining what kind of addition, change or update will be done to your access control
data.
If you are happy with your decision, simply confirm it.
start adding new authorizations to role superadmin.
- step 5 - confirm user added.
The user has now been added to this role. You can easily continue adding more users to this
role be restarting from step 2 or 3. You can also go directly to another area and keep working
on the same role.
start adding new authorizations to role superadmin.
- we are done
This example is very similar to all the other pages where you administrate WebAccess. The pages
are an easy gateway to maintaing access control rights and share a lot of features.
- divided into steps
- restart from any wanted step (not undo)
- changes must be confirmed
- link to other relevant areas
- prevent unwanted input
As an administrator with access to these pages you are free to manage the rights any way you want.
Here you can administrate the accounts and the access policy for your Invenio installation.
- Access policy:
To change the access policy, the general config file (or
access_control_config.py) must be edited manually in a text
editor. The site can there be defined as opened or closed, you can
edit the access policy level for guest accounts, registered
accounts and decide when to warn the owner of the account when
something happens with it, either when it is created, deleted or
approved. The Apache server must be restarted after modifying
these settings.
The two levels for guest account, are:
0 - Allow guest accounts
1 - Do not allow guest accounts
The five levels for normal accounts, are:
0 - Allow user to create account, automatically activate new accounts
1 - Allow user to create account, administrator must activate account
2 - Only administrators can create account. User cannot edit the email address.
3 - Users cannot register or update account information (email/password)
4 - User cannot change default login method
You can configure Invenio to send an email:
1. To an admin email-address when an account is created
2. To the owner of an account when it is created
3. To the owner of an account when it is activated
4. To the owner of an account when it is deleted
Define how open the site is:
0 = normal operation of the site
1 = read-only site, all write operations temporarily closed
2 = site fully closed
3 = database connections disabled
CFG_ACCESS_CONTROL_LEVEL_SITE = 0
Access policy for guests:
0 = Allow guests to search,
1 = Guests cannot search (all users must login)
CFG_ACCESS_CONTROL_LEVEL_GUESTS = 0
Access policy for accounts:
0 = Users can register, automatically acticate accounts
1 = Users can register, but admin must activate the accounts
2 = Users cannot register or change email address, only admin can register accounts.
3 = Users cannot register or update email address or password, only admin can register accounts.
4 = Same as 3, but user cannot change login method.
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS = 0
Limit email addresses available to use when register a new account (example: cern.ch):
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN = ""
Send an email when a new account is created by an user:
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS = 0
Send an email to the user notifying when the account is created:
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT = 0
Send an email to the user notifying when the account is activated:
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION = 0
Send an email to the user notifying when the account is deleted/rejected:
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION = 0
- Account overview:
Here you find an overview of the number of guest accounts, registered accounts and accounts
awaiting activation, with a link to the activation page.
- Create account:
For creating new accounts, the email address must be unique. If configured to do so, an email
will be sent to the given address when an account is created.
- Edit accounts:
For activating or rejecting accounts in addition to modifying them. An activated account can be
inactivated for a short period of time, but this will not warn the account owner. To find accounts
enter a part of the email address of the account and then search. This may take some time. If there
are more than the selected number of accounts per page, you can use the next/prev links to switch
pages. The accounts to search in can also be limited to only activated or not activated accounts.
- Edit account:
When editing one account, you can change the email address, password, delete the account, or modify
the baskets or alerts belonging to one account. Which login method should be the default for this
account can also be selected. To modify baskets or alerts, you need to login as the user, and
modify the desired data as a normal user. Remember to log out as the user when you are finished
editing.
Invenio supports using external login systems to authenticate users.
When a user wants to login, the username and password given by the user is checked against the selected
system, if the user is authenticated by the external system, a valid email-address is returned to
Invenio and used to recognize the user within Invenio.
If a new user is trying to login without having an account, using an external login system, an account
is automatically created in Invenio to recognize and store the users settings. The password for the
local account is randomly generated.
If you want the user to be unable to change login method and account username / password, forcing use
of certain external systems, set CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS to 4 as mentioned in the last paragraph.
If a user is changing login method from an external one to the internal, he also need to either change the
password before logging out, or set the password via the lost password email service.
If you are using CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS with a value greater than 1 note
that, even if the first login of a user through an external authentication technically means registering
the user into the system, this is not the semantic expected behaviour by the user. The user is already
registered at an authority that we trust, so there's no need to prevent the user from being imported
into the system. That's why for external authentication CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS is not
considered apart from what said above.
If a external login system is used, you may want to protect the users username / password using HTTPS.
To add new system, two changes must be made (for the time being):
- - The name of the method, if it is default or not, and the classname must be added to the variable
- CFG_EXTERNAL_AUTHENTICATION in access_control_config.py. Atleast one method must be marked as the
- default one. The internal login method should be given with None as classname.
-
- Example:
- CFG_EXTERNAL_AUTHENTICATION = {"%s (internal)" % CFG_SITE_NAME: (None, True), "CERN NICE (external)":
- (AuthCernWrapper(), False)}
+ - The name of the method, if it is default or not, and an instance of the class must be added to the variable
+ CFG_EXTERNAL_AUTHENTICATION in access_control_config.py.
- A class must be created derived from the class external_authentication inside file
external_authentication.py. This class must include at least the
function auth_user. This function returns a valid email-address in Invenio if the user
is authenticated, not necessarily the same entered by the user as username. If the user
is not authenticated, return None.
The class could also provide five more methods: fetch_user_preferences, user_exists,
fetch_user_groups_membership and fetch_all_users_groups_membership.
The first should take an email and eventually a password and should return a dictionary of keys
and value representing external preferences, infos or settings. If, for some reasons, you like
to force some kind of hiding for some particular field you should export the related key
prefixed by "HIDDEN_". Those fields won't be displayed in tables and pages regarding external
settings.
The second method should check through the external system if a particular email exists. If you
provide such a method then a user will be able to switch from and to this authorization method.
The third method should take an email and (if necessary) a password
and should return a dictionary of external_groups_names toghether with their description, for which
the user has a membership. Those groups will be merged into the groups system.
The user will be a member of those groups and will be able to use them in any place
where groups are useful, but won't be able to unsubscribe or to administrate them.
The fourth method should just return a dictionary of external groups as keys and tuples containing
a group description and a list of email of users belonging to each groups. Those memberships
will be merged into the database in the way done by the previous method, but could
provide batch synchronization of groups.
The fifth method should just return the nickname as is known by the external authentication
system, given the usual email/username and the password.
Note: if your system has more than one external login methods then incoherence in the groups
memberships could happen when a user switch his login method. This will be fixed some times in the
future.
If you add as an attribute of your class the enforce_external_nicknames and set it to True, this will enforce
the system to import external nicknames whenever the user login with the external login method for the
first time. Since a nickname is not changable this will stay fixed forever. If this nickname is
already registered in the system (suppose that is linked with a local account) then it will not be
imported. If this variable doesn't exist or is set to False then no nickname will be
imported and the user will be free to choose a nickname in the future (and then this will again
stay forever).
Note: every method will receive as last parameter the mod_python request object, that could
be used for particular purposes.
Example template:
from invenio.external_authentication import ExternalAuth, InvenioWebAccessExternalAuthError
class ExternalAuthFoo(ExternalAuth):
"""External authentication template example."""
def __init__ (self):
"""Initialize stuff here."""
self.name = None
self.enforce_external_nicknames = False
pass
def auth_user(self, username, password, req=None):
"""Authenticate user-supplied USERNAME and PASSWORD.
Return None if authentication failed, or the email address of the
person if the authentication was successful. In order to do
this you may perhaps have to keep a translation table between
usernames and email addresses.
Raise InvenioWebAccessExternalAuthError in case of external troubles.
"""
raise NotImplementedError
#return None
def user_exists(self, email, req=None):
"""Checks against external_authentication for existance of email.
@return True if the user exists, False otherwise
"""
raise NotImplementedError
def fetch_user_groups_membership(self, username, password=None, req=None):
"""Given a username, returns a dictionary of groups
and their description to which the user is subscribed.
Raise InvenioWebAccessExternalAuthError in case of troubles.
"""
raise NotImplementedError
#return {}
def fetch_user_preferences(self, username, password=None, req=None):
"""Given a username and a password, returns a dictionary of keys and
values, corresponding to external infos and settings.
userprefs = {"telephone": "2392489",
"address": "10th Downing Street"}
"""
raise NotImplementedError
#return {}
def fetch_all_users_groups_membership(self, req=None):
"""Fetch all the groups with a description, and users who belong to
each groups.
@return {'mygroup': ('description', ['email1', 'email2', ...]), ...}
"""
raise NotImplementedError
def fetch_user_nickname(self, username, password, req=None):
"""Given a username and a password, returns the right nickname belonging
to that user (username could be an email).
"""
raise NotImplementedError
#return Nickname
In the WebAccess RBAC system, roles are built up from their names,
description and definition.
A definition is the way to formally implicitly define which users belong
to which roles.
A definition is expressed in a firewall like rules language. It's built up
by rows which are matched from top to bottom, in order to decide if the
current user (wethever he/she is logged in or not) may belong to a role.
Any row has this syntax:
ALLOW/DENY ANY/ALL
or
ALLOW/DENY FROM/UNTIL "YYYY-MM-DD"ALLOW/DENY [NOT] field {one or more values}
The rows are parsed from top to bottom. If a row matches the user than the
user belongs to the role if the rule is an ALLOW rule, otherwise, if the
rule is a DENY one, the user doesn't belong to the role.
A rule of the kind ALLOW|DENY ANY always matches, regardless of the user.
Note, in place of ANY you can use the word ALL. The semantic is the same. The
system support both to let the user comply with the English grammar.
The second type of rule is interpreted as follows: given a date in the
form "YYYY-MM-DD" (double-)quoted), the rule is matched if, when using FROM,
the current date is either identical or 'bigger' than the given date, or if,
when using UNTIL, the current date is either identical or 'smaller' than the
given date. If the rule starts with ALLOW and is matched
then the next row is evaluated. If it is not matched, then the whole FireRole
will evaluate into a DENY ALL. If the rule starts with DENY and
is matched then the whole FireRole while evaluate as a DENY ALL. If it is
not matched then the next row is evaluated.
The third type of rule is interpreted as follows: given a dictionary
of keys:values describing a user (we will cover this below), the rule
considers the value associated with the key named in field, and checks
if it corresponds to at least one of the values in the "one or more values" list.
This is a list of comma separated strings, which can be literal
(double-)quoted strings or regexps (marked by `/' ... `/' signs). If at
least a value matches (literally or through the regexp language), the
whole rule is considered to match.
If the optional NOT keyword is specified than if at least a value of the
rule matches the rule is skipped, otherwise if all the value of the rules
don't match the whole rule matches.
A DENY ALL rule is implicitly added at the end of every definition. Note that
this imply that, if you are using e.g. a temporal rule (FROM/UNTIL), you
should explicitly add an additional row with value ALLOW ANY,
if you actually want to allow users in the specified timeframe.
Any field is valid, but only rules concerning fields which currently
exist in the user describing dictionary are checked. All the rules
with non existant fields are skipped.
The user describing dictionary (user_info) is built at runtime with all the informations
that can be gathered about the current user (and its session).
- Currently valid fields are: uid, email, nickname, apache_user, remote_ip,
- remote_host, groups, apache_groups and all the external settings provided
+ Currently valid fields are: uid, email, nickname, remote_ip,
+ remote_host, groups and all the external settings provided
by the external authentication systems (e.g. CERN SSO provides:
external_authmethod, external_building, external_department, external_email,
external_external, external_firstname, external_fullname, external_homdir,
external_homeinstitute, external_lastname, external_login, external_mobilenumber,
external_phonenumber).
Among those fields there are some special cases, which are remote_ip and
(apache_)groups. Rules can refer to remote_ip either using a literal
expression for specifing list of single ips, or a usual regexp (or list
of regexps), or, also, using the common network group/mask notation
(e.g. "127.0.0.0/24") as a literal string, which is a mix between literal
expressions and regexps. (apache_)groups are related to group memberships.
Since a user will probably belong to more than a group, then the rule
matches if there's at least one group to which the user belong, that matches
at least one of the expressions (NOT rules behave as you can imagine).
The dictionary is built using the current user session. If the user is
authenticated in some way (apache, locally, externally, SSO...) then more
infos could be provided to the firerole system in order to decide if the
user should belong to a role or not.
The default fields that are always there are:
uid: an integer representing the user id
nickname: the nickname of the user
email: the email of the user
group/groups: local or external group to which the user belong
guest: 1 if the user is a guest (not logged), 0 otherwise
plus all the external setting retrieved by an external authentication system.
If the action to which the role defined is raised from the webinterface of
Invenio, then you will have those additional fields:
remote_ip: the remote ip address of the user who is browsing
remote_host: the remote hostname of the user who is browsing
referer: the webpage from where the user is coming from
uri: the uri the user is visiting
agent: the agent string describing the user's browser
-
apache_user: the Apache user provided by the authenticated user
-
apache_group/apache_groups: the Apache groups to which the apache user
- belong
Note that you can specify either (apache_)group or (apache_)groups (with or
without the trailing s). They are semantically equal and are supported just
to let people comply with the English grammar.
Every rule is case-insensitive (apart values which must match literally
and regexp values which don't explicitly specify case-insesitive matches).
Every rule may contain comments preceded by the '#' character.
Any comment is discarded.
When you set a definition for a role, it is actually compiled and stored
in a binary compressed form inside the database. If the syntax isn't correct
this will be stated and the definition won't be set or updated.
Example of role definition:
allow not email /.*@gmail.com/,/.*@hotmail.com/
deny group badguys
allow remote_ip "127.0.0.0/24"
deny all
This definition would match all users whose emails don't end with @gmail.com and
@hotmail.com, or who don't belong to the group badguys and have remote_ip
in the 24bit mask network of 127.0.0.0. All the the other users don't belong
to the role which is being defined.
If you want to discover which keys are available on your system to build a FireRole
rule, just login with your account in your installation and visit your account page,
by activating verbose=9 variable. Under the tile you will se the available keys and
values that you can use to build a FireRole rule. All but fields prefixed with
precached_ are usuable.
diff --git a/modules/webaccess/lib/Makefile.am b/modules/webaccess/lib/Makefile.am
index be8f3035a..43b95c43c 100644
--- a/modules/webaccess/lib/Makefile.am
+++ b/modules/webaccess/lib/Makefile.am
@@ -1,39 +1,40 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
pylibdir = $(libdir)/python/invenio
pylib_DATA = access_control_engine.py \
access_control_config.py \
access_control_admin.py \
access_control_mailcookie.py \
access_control_firerole.py \
access_control_firerole_tests.py \
webaccessadmin_lib.py \
external_authentication_cern.py \
external_authentication.py \
external_authentication_ldap.py \
external_authentication_cern_wrapper.py \
external_authentication_cern_tests.py \
external_authentication_sso.py \
+ external_authentication_robot.py \
webaccess_regression_tests.py
noinst_DATA = collection_restrictions_migration_kit.py
EXTRA_DIST = $(pylib_DATA) $(noinst_DATA)
CLEANFILES = *~ *.tmp *.pyc
diff --git a/modules/webaccess/lib/access_control_config.py b/modules/webaccess/lib/access_control_config.py
index 8309e0869..140f74fb9 100644
--- a/modules/webaccess/lib/access_control_config.py
+++ b/modules/webaccess/lib/access_control_config.py
@@ -1,296 +1,317 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio Access Control Config. """
__revision__ = \
"$Id$"
# pylint: disable=C0301
from invenio.config import CFG_SITE_NAME, CFG_SITE_URL, CFG_SITE_LANG, \
- CFG_SITE_SECURE_URL, CFG_SITE_SUPPORT_EMAIL, CFG_CERN_SITE
+ CFG_SITE_SECURE_URL, CFG_SITE_SUPPORT_EMAIL, CFG_CERN_SITE, \
+ CFG_OPENAIRE_SITE
from invenio.messages import gettext_set_language
class InvenioWebAccessFireroleError(Exception):
"""Just an Exception to discover if it's a FireRole problem"""
pass
# VALUES TO BE EXPORTED
# CURRENTLY USED BY THE FILES access_control_engine.py access_control_admin.py webaccessadmin_lib.py
# name of the role giving superadmin rights
SUPERADMINROLE = 'superadmin'
# name of the webaccess webadmin role
WEBACCESSADMINROLE = 'webaccessadmin'
# name of the action allowing roles to access the web administrator interface
WEBACCESSACTION = 'cfgwebaccess'
# name of the action allowing roles to access the web administrator interface
VIEWRESTRCOLL = 'viewrestrcoll'
# name of the action allowing roles to delegate the rights to other roles
# ex: libraryadmin to delegate libraryworker
DELEGATEADDUSERROLE = 'accdelegaterole'
# max number of users to display in the drop down selects
MAXSELECTUSERS = 25
# max number of users to display in a page (mainly for user area)
MAXPAGEUSERS = 25
# default role definition, source:
CFG_ACC_EMPTY_ROLE_DEFINITION_SRC = 'deny all'
# default role definition, compiled:
-CFG_ACC_EMPTY_ROLE_DEFINITION_OBJ = (False, False, ())
+CFG_ACC_EMPTY_ROLE_DEFINITION_OBJ = (False, ())
# default role definition, compiled and serialized:
CFG_ACC_EMPTY_ROLE_DEFINITION_SER = None
# List of tags containing (multiple) emails of users who should authorize
# to access the corresponding record regardless of collection restrictions.
if CFG_CERN_SITE:
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS = ['859__f', '270__m']
else:
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS = ['8560_f']
# Use external source for access control?
-# Atleast one must be added
-# Adviced not to change the name, since it is used to identify the account
-# Format is: System name: (System class, Default True/Flase), atleast one
-# must be default
-CFG_EXTERNAL_AUTHENTICATION = {"Local" : (None, True)}
-
-# Variables to set to the SSO Authentication name if using SSO
-CFG_EXTERNAL_AUTH_USING_SSO = False
-CFG_EXTERNAL_AUTH_LOGOUT_SSO = None
+# CFG_EXTERNAL_AUTHENTICATION -- this is a dictionary with the enabled login method.
+# The key is the name of the login method and the value is an instance of
+# of the login method (see /help/admin/webaccess-admin-guide#5). Set the value
+# to None if you wish to use the local Invenio authentication method.
+# CFG_EXTERNAL_AUTH_DEFAULT -- set this to the key in CFG_EXTERNAL_AUTHENTICATION
+# that should be considered as default login method
+# CFG_EXTERNAL_AUTH_USING_SSO -- set this to the login method name of an SSO
+# login method, if any, otherwise set this to None.
+# CFG_EXTERNAL_AUTH_LOGOUT_SSO -- if CFG_EXTERNAL_AUTH_USING_SSO was not None
+# set this to the URL that should be contacted to perform an SSO logout
+
+from invenio.external_authentication_robot import ExternalAuthRobot
if CFG_CERN_SITE:
- if True:
- import external_authentication_sso as ea_sso
- CFG_EXTERNAL_AUTH_USING_SSO = "CERN"
- # Link to reach in order to logout from SSO
- CFG_EXTERNAL_AUTH_LOGOUT_SSO = 'https://login.cern.ch/adfs/ls/?wa=wsignout1.0'
- CFG_EXTERNAL_AUTHENTICATION = {CFG_EXTERNAL_AUTH_USING_SSO : (ea_sso.ExternalAuthSSO(), True)}
- else:
- import external_authentication_cern as ea_cern
- CFG_EXTERNAL_AUTHENTICATION = {"Local": (None, False), \
- "CERN": (ea_cern.ExternalAuthCern(), True)}
+ import external_authentication_sso as ea_sso
+ CFG_EXTERNAL_AUTH_USING_SSO = "CERN"
+ CFG_EXTERNAL_AUTH_DEFAULT = CFG_EXTERNAL_AUTH_USING_SSO
+ CFG_EXTERNAL_AUTH_LOGOUT_SSO = 'https://login.cern.ch/adfs/ls/?wa=wsignout1.0'
+ CFG_EXTERNAL_AUTHENTICATION = {
+ CFG_EXTERNAL_AUTH_USING_SSO : ea_sso.ExternalAuthSSO(),
+ }
+elif CFG_OPENAIRE_SITE:
+ CFG_EXTERNAL_AUTH_DEFAULT = 'Local'
+ CFG_EXTERNAL_AUTH_USING_SSO = False
+ CFG_EXTERNAL_AUTH_LOGOUT_SSO = None
+ CFG_EXTERNAL_AUTHENTICATION = {
+ "Local": None,
+ "OpenAIRE": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=False),
+ "ZOpenAIRE": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=True)
+ }
+else:
+ CFG_EXTERNAL_AUTH_DEFAULT = 'Local'
+ CFG_EXTERNAL_AUTH_USING_SSO = False
+ CFG_EXTERNAL_AUTH_LOGOUT_SSO = None
+ CFG_EXTERNAL_AUTHENTICATION = {
+ "Local": None,
+ "Robot": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=False),
+ "ZRobot": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=True)
+ }
+
# default data for the add_default_settings function
# Note: by default the definition is set to deny any. This won't be a problem
# because userid directly connected with roles will still be allowed.
# roles
# name description definition
DEF_ROLES = ((SUPERADMINROLE, 'superuser with all rights', 'deny any'),
(WEBACCESSADMINROLE, 'WebAccess administrator', 'deny any'),
('anyuser', 'Any user', 'allow any'),
('basketusers', 'Users who can use baskets', 'allow any'),
('loanusers', 'Users who can use loans', 'allow any'),
('groupusers', 'Users who can use groups', 'allow any'),
('alertusers', 'Users who can use alerts', 'allow any'),
('messageusers', 'Users who can use messages', 'allow any'),
('holdingsusers', 'Users who can view holdings', 'allow any'),
('statisticsusers', 'Users who can view statistics', 'allow any'))
# Demo site roles
DEF_DEMO_ROLES = (('photocurator', 'Photo collection curator', 'deny any'),
- ('thesesviewer', 'Theses viewer', 'allow group "Theses viewers"\nallow apache_group "theses"'),
- ('swordcurator', 'BibSword client curator', 'deny any'),
+ ('thesesviewer', 'Theses viewer', 'allow group "Theses viewers"'),
('thesescurator', 'Theses collection curator', 'deny any'),
+ ('swordcurator', 'BibSword client curator', 'deny any'),
('referee_DEMOBOO_*', 'Book collection curator', 'deny any'),
('restrictedpicturesviewer', 'Restricted pictures viewer', 'deny any'),
('curator', 'Curator', 'deny any'),
('basketusers', 'Users who can use baskets', 'deny email "hyde@cds.cern.ch"\nallow any'),
('submit_DEMOJRN_*', 'Users who can submit (and modify) "Atlantis Times" articles', 'deny all'),
('atlantiseditor', 'Users who can configure "Atlantis Times" journal', 'deny all'),
('commentmoderator', 'Users who can moderate comments', 'deny all'),
('poetrycommentreader', 'Users who can view comments in Poetry collection', 'deny all'))
DEF_DEMO_USER_ROLES = (('jekyll@cds.cern.ch', 'thesesviewer'),
('jekyll@cds.cern.ch', 'swordcurator'),
('dorian.gray@cds.cern.ch', 'referee_DEMOBOO_*'),
('balthasar.montague@cds.cern.ch', 'curator'),
('romeo.montague@cds.cern.ch', 'restrictedpicturesviewer'),
('romeo.montague@cds.cern.ch', 'swordcurator'),
('romeo.montague@cds.cern.ch', 'thesescurator'),
('juliet.capulet@cds.cern.ch', 'restrictedpicturesviewer'),
('juliet.capulet@cds.cern.ch', 'photocurator'),
('romeo.montague@cds.cern.ch', 'submit_DEMOJRN_*'),
('juliet.capulet@cds.cern.ch', 'submit_DEMOJRN_*'),
('balthasar.montague@cds.cern.ch', 'atlantiseditor'),
('romeo.montague@cds.cern.ch', 'poetrycommentreader'))
# users
# list of e-mail addresses
DEF_USERS = []
# actions
# name desc allowedkeywords optional
DEF_ACTIONS = (
('cfgwebsearch', 'configure WebSearch', '', 'no'),
('cfgbibformat', 'configure BibFormat', '', 'no'),
('cfgbibknowledge', 'configure BibKnowledge', '', 'no'),
('cfgwebsubmit', 'configure WebSubmit', '', 'no'),
('cfgbibrank', 'configure BibRank', '', 'no'),
('cfgwebcomment', 'configure WebComment', '', 'no'),
('cfgoaiharvest', 'configure OAI Harvest', '', 'no'),
('cfgoairepository', 'configure OAI Repository', '', 'no'),
('cfgbibindex', 'configure BibIndex', '', 'no'),
('cfgbibexport', 'configure BibExport', '', 'no'),
+ ('cfgrobotkeys', 'configure Robot keys', 'login_method,robot', 'yes'),
('runbibindex', 'run BibIndex', '', 'no'),
('runbibupload', 'run BibUpload', '', 'no'),
('runwebcoll', 'run webcoll', 'collection', 'yes'),
('runbibformat', 'run BibFormat', 'format', 'yes'),
('runbibclassify', 'run BibClassify', 'taxonomy', 'yes'),
('runbibtaskex', 'run BibTaskEx example', '', 'no'),
('runbibrank', 'run BibRank', '', 'no'),
('runoaiharvest', 'run oaiharvest task', '', 'no'),
('runoairepository', 'run oairepositoryupdater task', '', 'no'),
('runbibedit', 'run Record Editor', 'collection', 'yes'),
('runbibeditmulti', 'run Multi-Record Editor', '', 'no'),
('runbibdocfile', 'run Document File Manager', '', 'no'),
('runbibmerge', 'run Record Merger', '', 'no'),
('runbibswordclient', 'run BibSword client', '', 'no'),
('runwebstatadmin', 'run WebStadAdmin', '', 'no'),
('runinveniogc', 'run InvenioGC', '', 'no'),
('runbibexport', 'run BibExport', '', 'no'),
('referee', 'referee document type doctype/category categ', 'doctype,categ', 'yes'),
('submit', 'use webSubmit', 'doctype,act,categ', 'yes'),
('viewrestrdoc', 'view restricted document', 'status', 'no'),
('viewrestrcomment', 'view restricted comment', 'status', 'no'),
(WEBACCESSACTION, 'configure WebAccess', '', 'no'),
(DELEGATEADDUSERROLE, 'delegate subroles inside WebAccess', 'role', 'no'),
(VIEWRESTRCOLL, 'view restricted collection', 'collection', 'no'),
('cfgwebjournal', 'configure WebJournal', 'name,with_editor_rights', 'no'),
('viewcomment', 'view comments', 'collection', 'no'),
('sendcomment', 'send comments', 'collection', 'no'),
('attachcommentfile', 'attach files to comments', 'collection', 'no'),
('attachsubmissionfile', 'upload files to drop box during submission', '', 'no'),
('cfgbibexport', 'configure BibExport', '', 'no'),
('runbibexport', 'run BibExport', '', 'no'),
('usebaskets', 'use baskets', '', 'no'),
('useloans', 'use loans', '', 'no'),
('usegroups', 'use groups', '', 'no'),
('usealerts', 'use alerts', '', 'no'),
('usemessages', 'use messages', '', 'no'),
('viewholdings', 'view holdings', 'collection', 'yes'),
('viewstatistics', 'view statistics', 'collection', 'yes'),
('runbibcirculation', 'run BibCirculation', '', 'no'),
('moderatecomments', 'moderate comments', 'collection', 'no'),
('runbatchuploader', 'run batchuploader', 'collection', 'yes')
)
# Default authorizations
# role action arguments
DEF_AUTHS = (('basketusers', 'usebaskets', {}),
('loanusers', 'useloans', {}),
('groupusers', 'usegroups', {}),
('alertusers', 'usealerts', {}),
('messageusers', 'usemessages', {}),
('holdingsusers', 'viewholdings', {}),
('statisticsusers', 'viewstatistics', {}))
# Demo site authorizations
# role action arguments
DEF_DEMO_AUTHS = (
('photocurator', 'runwebcoll', {'collection': 'Pictures'}),
('restrictedpicturesviewer', 'viewrestrdoc', {'status': 'restricted_picture'}),
('thesesviewer', VIEWRESTRCOLL, {'collection': 'Theses'}),
('referee_DEMOBOO_*', 'referee', {'doctype': 'DEMOBOO', 'categ': '*'}),
('curator', 'cfgbibknowledge', {}),
('curator', 'runbibedit', {}),
('curator', 'runbibeditmulti', {}),
('curator', 'runbibmerge', {}),
('swordcurator', 'runbibswordclient', {}),
('thesescurator', 'runbibedit', {'collection': 'Theses'}),
('thesescurator', VIEWRESTRCOLL, {'collection': 'Theses'}),
('photocurator', 'runbibedit', {'collection': 'Pictures'}),
('referee_DEMOBOO_*', 'runbibedit', {'collection': 'Books'}),
('submit_DEMOJRN_*', 'submit', {'doctype': 'DEMOJRN', 'act': 'SBI', 'categ': '*'}),
('submit_DEMOJRN_*', 'submit', {'doctype': 'DEMOJRN', 'act': 'MBI', 'categ': '*'}),
('submit_DEMOJRN_*', 'cfgwebjournal', {'name': 'AtlantisTimes', 'with_editor_rights': 'no'}),
('atlantiseditor', 'cfgwebjournal', {'name': 'AtlantisTimes', 'with_editor_rights': 'yes'}),
('referee_DEMOBOO_*', 'runbatchuploader', {'collection': 'Books'}),
('poetrycommentreader', 'viewcomment', {'collection': 'Poetry'})
)
_ = gettext_set_language(CFG_SITE_LANG)
# Activities (i.e. actions) for which exists an administrative web interface.
CFG_ACC_ACTIVITIES_URLS = {
'runbibedit' : (_("Run Record Editor"), "%s/record/edit/?ln=%%s" % CFG_SITE_URL),
'runbibeditmulti' : (_("Run Multi-Record Editor"), "%s/record/multiedit/?ln=%%s" % CFG_SITE_URL),
'runbibdocfile' : (_("Run Document File Manager"), "%s/submit/managedocfiles?ln=%%s" % CFG_SITE_URL),
'runbibmerge' : (_("Run Record Merger"), "%s/record/merge/?ln=%%s" % CFG_SITE_URL),
'runbibswordclient' : (_("Run BibSword client"), "%s/bibsword/?ln=%%s" % CFG_SITE_URL),
'cfgbibknowledge' : (_("Configure BibKnowledge"), "%s/kb?ln=%%s" % CFG_SITE_URL),
'cfgbibformat' : (_("Configure BibFormat"), "%s/admin/bibformat/bibformatadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgoaiharvest' : (_("Configure OAI Harvest"), "%s/admin/bibharvest/oaiharvestadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgoairepository' : (_("Configure OAI Repository"), "%s/admin/bibharvest/oairepositoryadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgbibindex' : (_("Configure BibIndex"), "%s/admin/bibindex/bibindexadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgbibrank' : (_("Configure BibRank"), "%s/admin/bibrank/bibrankadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebaccess' : (_("Configure WebAccess"), "%s/admin/webaccess/webaccessadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebcomment' : (_("Configure WebComment"), "%s/admin/webcomment/webcommentadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebsearch' : (_("Configure WebSearch"), "%s/admin/websearch/websearchadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebsubmit' : (_("Configure WebSubmit"), "%s/admin/websubmit/websubmitadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebjournal' : (_("Configure WebJournal"), "%s/admin/webjournal/webjournaladmin.py?ln=%%s" % CFG_SITE_URL),
'runbibcirculation' : (_("Run BibCirculation"), "%s/admin/bibcirculation/bibcirculationadmin.py?ln=%%s" % CFG_SITE_URL),
'runbatchuploader' : (_("Run Batch Uploader"), "%s/batchuploader/metadata?ln=%%s" % CFG_SITE_URL)
}
CFG_WEBACCESS_MSGS = {
0: 'Try to login with another account.' % (CFG_SITE_SECURE_URL),
1: ' If you think this is not correct, please contact: %s' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
2: ' If you have any questions, please write to %s' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
3: 'Guest users are not allowed, please login.' % CFG_SITE_SECURE_URL,
4: 'The site is temporarily closed for maintenance. Please come back soon.',
5: 'Authorization failure',
6: '%s temporarily closed' % CFG_SITE_NAME,
7: 'This functionality is temporarily closed due to server maintenance. Please use only the search engine in the meantime.',
8: 'Functionality temporarily closed'
}
CFG_WEBACCESS_WARNING_MSGS = {
0: 'Authorization granted',
1: 'You are not authorized to perform this action.',
2: 'You are not authorized to perform any action.',
3: 'The action %s does not exist.',
4: 'Unexpected error occurred.',
5: 'Missing mandatory keyword argument(s) for this action.',
6: 'Guest accounts are not authorized to perform this action.',
7: 'Not enough arguments, user ID and action name required.',
8: 'Incorrect keyword argument(s) for this action.',
9: """Account '%s' is not yet activated.""",
10: """You were not authorized by the authentication method '%s'.""",
11: """The selected login method '%s' is not the default method for this account, please try another one.""",
12: """Selected login method '%s' does not exist.""",
13: """Could not register '%s' account.""",
14: """Could not login using '%s', because this user is unknown.""",
15: """Could not login using your '%s' account, because you have introduced a wrong password.""",
16: """External authentication troubles using '%s' (maybe temporary network problems).""",
17: """You have not yet confirmed the email address for the '%s' authentication method.""",
18: """The administrator has not yet activated your account for the '%s' authentication method.""",
19: """The site is having troubles in sending you an email for confirming your email address. The error has been logged and will be taken care of as soon as possible.""",
20: """No roles are authorized to perform action %s with the given parameters."""
}
diff --git a/modules/webaccess/lib/access_control_engine.py b/modules/webaccess/lib/access_control_engine.py
index 356962c49..ea5dc625f 100644
--- a/modules/webaccess/lib/access_control_engine.py
+++ b/modules/webaccess/lib/access_control_engine.py
@@ -1,148 +1,90 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio Access Control Engine in mod_python."""
__revision__ = "$Id$"
import cgi
import sys
from urllib import quote
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from invenio.config import CFG_SITE_SECURE_URL
from invenio.dbquery import run_sql
from invenio.access_control_admin import acc_find_possible_roles, acc_is_user_in_role, CFG_SUPERADMINROLE_ID, acc_get_role_users
from invenio.access_control_config import CFG_WEBACCESS_WARNING_MSGS, CFG_WEBACCESS_MSGS
from invenio.webuser import collect_user_info
-from invenio.access_control_firerole import acc_firerole_suggest_apache_p, deserialize, load_role_definition, acc_firerole_extract_emails
+from invenio.access_control_firerole import deserialize, load_role_definition, acc_firerole_extract_emails
from invenio.urlutils import make_canonical_urlargd
-CFG_CALLED_FROM_APACHE = 1 #1=web,0=cli
-try:
- import _apache
-except ImportError, e:
- CFG_CALLED_FROM_APACHE = 0
-
-def make_list_apache_firerole(name_action, arguments):
- """Given an action and a dictionary arguments returns a list of all the
- roles (and their descriptions) which are authorized to perform this
- action with these arguments, and whose FireRole definition expect
- an Apache Password membership.
- """
- roles = acc_find_possible_roles(name_action, **arguments)
-
- ret = []
-
- for role in roles:
- res = run_sql('SELECT name, description, firerole_def_ser FROM accROLE WHERE id=%s', (role, ))
- if acc_firerole_suggest_apache_p(deserialize(res[0][2])):
- ret.append((res[0][0], res[0][1]))
- return ret
-
-def _format_list_of_apache_firerole(roles, referer):
- """Given a list of tuples (role, description) (returned by make_list_apache_firerole), and a referer url, returns a nice string for
- presenting urls that let the user login with Apache password through
- Firerole.
- This function is needed only at CERN for aiding in the migration of
- Apache Passwords restricted collections to FireRole roles.
- Please use it with care."""
- out = ""
- if roles:
- out += "
1) Here is a list of administrative roles you may have " \
- "received authorization for via an Apache password. If you are aware " \
- "of such a password, please follow the corresponding link:"
- out += "
"
- return out
-
-def make_apache_message(name_action, arguments, referer=None):
- """Given an action name and a dictionary of arguments and a refere url
- it returns a a nice string for presenting urls that let the user login
- with Apache password through Firerole authorized roles.
- This function is needed only at CERN for aiding in the migration of
- Apache Passwords restricted collections to FireRole roles.
- Please use it with care."""
- if not referer:
- referer = '%s/youraccount/youradminactivities' % CFG_SITE_SECURE_URL
- roles = make_list_apache_firerole(name_action, arguments)
- if roles:
- return _format_list_of_apache_firerole(roles, referer)
- else:
- return ""
-
def acc_authorize_action(req, name_action, authorized_if_no_roles=False, **arguments):
"""
Given the request object (or the user_info dictionary, or the uid), checks
if the user is allowed to run name_action with the given parameters.
If authorized_if_no_roles is True and no role exists (different
than superadmin) that are authorized to execute the given action, the
authorization will be granted.
Returns (0, msg) when the authorization is granted, (1, msg) when it's not.
"""
user_info = collect_user_info(req)
roles = acc_find_possible_roles(name_action, always_add_superadmin=False, **arguments)
for id_role in roles:
if acc_is_user_in_role(user_info, id_role):
## User belong to at least one authorized role.
return (0, CFG_WEBACCESS_WARNING_MSGS[0])
if acc_is_user_in_role(user_info, CFG_SUPERADMINROLE_ID):
## User is SUPERADMIN
return (0, CFG_WEBACCESS_WARNING_MSGS[0])
if not roles:
## No role is authorized for the given action/arguments
if authorized_if_no_roles:
## User is authorized because no authorization exists for the given
## action/arguments
return (0, CFG_WEBACCESS_WARNING_MSGS[0])
else:
## User is not authorized.
return (20, CFG_WEBACCESS_WARNING_MSGS[20] % cgi.escape(name_action))
## User is not authorized
- return (1, "%s %s %s" % (CFG_WEBACCESS_WARNING_MSGS[1], (CFG_CALLED_FROM_APACHE and "%s %s" % (CFG_WEBACCESS_MSGS[0] % quote(user_info['uri']), CFG_WEBACCESS_MSGS[1]) or ""), make_apache_message(name_action, arguments, user_info['uri'])))
+ in_a_web_request_p = bool(user_info['uri'])
+ return (1, "%s %s" % (CFG_WEBACCESS_WARNING_MSGS[1], (in_a_web_request_p and "%s %s" % (CFG_WEBACCESS_MSGS[0] % quote(user_info['uri']), CFG_WEBACCESS_MSGS[1]) or "")))
def acc_get_authorized_emails(name_action, **arguments):
"""
Given the action and its arguments, try to retireve all the matching
email addresses of users authorized.
This is a best effort operation, because if a role is authorized and
happens to be defined using a FireRole rule based on regular expression
or on IP addresses, non every email might be returned.
@param name_action: the name of the action.
@type name_action: string
@param arguments: the arguments to the action.
@return: the list of authorized emails.
@rtype: set of string
"""
authorized_emails = set()
roles = acc_find_possible_roles(name_action, always_add_superadmin=False, **arguments)
for id_role in roles:
for dummy1, email, dummy2 in acc_get_role_users(id_role):
authorized_emails.add(email.lower().strip())
firerole = load_role_definition(id_role)
authorized_emails.union(acc_firerole_extract_emails(firerole))
return authorized_emails
diff --git a/modules/webaccess/lib/access_control_firerole.py b/modules/webaccess/lib/access_control_firerole.py
index 1344dbc3a..098cce844 100644
--- a/modules/webaccess/lib/access_control_firerole.py
+++ b/modules/webaccess/lib/access_control_firerole.py
@@ -1,348 +1,336 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio Access Control FireRole."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
"""These functions are for realizing a firewall like role definition for extending
webaccess to connect user to roles using every infos about users.
"""
import re
import cPickle
from zlib import compress, decompress
import sys
import time
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from invenio.access_control_config import InvenioWebAccessFireroleError
from invenio.dbquery import run_sql, blob_to_string
from invenio.config import CFG_CERN_SITE
from invenio.access_control_config import CFG_ACC_EMPTY_ROLE_DEFINITION_SRC, \
CFG_ACC_EMPTY_ROLE_DEFINITION_SER, CFG_ACC_EMPTY_ROLE_DEFINITION_OBJ
# INTERFACE
def compile_role_definition(firerole_def_src):
""" Given a text in which every row contains a rule it returns the compiled
object definition.
Rules have the following syntax:
allow|deny [not] field {list of one or more (double)quoted string or regexp}
or allow|deny any
Every row may contain a # sign followed by a comment which are discarded.
Field could be any key contained in a user_info dictionary. If the key does
not exist in the dictionary, the rule is skipped.
The first rule which matches return.
"""
line = 0
ret = []
default_allow_p = False
- suggest_apache_p = False
if not firerole_def_src or not firerole_def_src.strip():
firerole_def_src = CFG_ACC_EMPTY_ROLE_DEFINITION_SRC
for row in firerole_def_src.split('\n'):
line += 1
row = row.strip()
if not row:
continue
clean_row = _no_comment_re.sub('', row)
if clean_row:
g = _any_rule_re.match(clean_row)
if g:
default_allow_p = g.group('command').lower() == 'allow'
break
g = _rule_re.match(clean_row)
if g:
allow_p = g.group('command').lower() == 'allow'
not_p = g.group('not') != None
field = g.group('field').lower()
- # Renaming groups to group and apache_groups to apache_group
+ # Renaming groups to group
for alias_item in _aliasTable:
if field in alias_item:
field = alias_item[0]
break
if field.startswith('precached_'):
raise InvenioWebAccessFireroleError("Error while compiling rule %s (line %s): %s is a reserved key and can not be used in FireRole rules!" % (row, line, field))
expressions = g.group('expression')+g.group('more_expressions')
expressions_list = []
for expr in _expressions_re.finditer(expressions):
expr = expr.group()
if field in ('from', 'until'):
try:
expressions_list.append((False, time.mktime(time.strptime(expr[1:-1], '%Y-%m-%d'))))
except Exception, msg:
raise InvenioWebAccessFireroleError("Syntax error while compiling rule %s (line %s): %s is not a valid date with format YYYY-MM-DD because %s!" % (row, line, expr, msg))
elif expr[0] == '/':
try:
expressions_list.append((True, re.compile(expr[1:-1], re.I)))
except Exception, msg:
raise InvenioWebAccessFireroleError("Syntax error while compiling rule %s (line %s): %s is not a valid re because %s!" % (row, line, expr, msg))
else:
if field == 'remote_ip' and '/' in expr[1:-1]:
try:
expressions_list.append((False, _ip_matcher_builder(expr[1:-1])))
except Exception, msg:
raise InvenioWebAccessFireroleError("Syntax error while compiling rule %s (line %s): %s is not a valid ip group because %s!" % (row, line, expr, msg))
else:
expressions_list.append((False, expr[1:-1]))
expressions_list = tuple(expressions_list)
if field in ('from', 'until'):
if len(expressions_list) != 1:
raise InvenioWebAccessFireroleError("Error when compiling rule %s (line %s): exactly one date is expected when using 'from' or 'until', but %s were found" % (row, line, len(expressions_list)))
if not_p:
raise InvenioWebAccessFireroleError("Error when compiling rule %s (line %s): 'not' is not allowed when using 'from' or 'until'" % (row, line))
- elif field in ('apache_group', 'apache_user'):
- suggest_apache_p = True
ret.append((allow_p, not_p, field, expressions_list))
else:
raise InvenioWebAccessFireroleError("Syntax error while compiling rule %s (line %s): not a valid rule!" % (row, line))
- return (default_allow_p, suggest_apache_p, tuple(ret))
+ return (default_allow_p, tuple(ret))
def repair_role_definitions():
""" Try to rebuild compiled serialized definitions from their respectives
sources. This is needed in case Python break back compatibility.
"""
definitions = run_sql("SELECT id, firerole_def_src FROM accROLE")
for role_id, firerole_def_src in definitions:
run_sql("UPDATE accROLE SET firerole_def_ser=%s WHERE id=%s", (serialize(compile_role_definition(firerole_def_src)), role_id))
def store_role_definition(role_id, firerole_def_ser, firerole_def_src):
""" Store a compiled serialized definition and its source in the database
alongside the role to which it belong.
@param role_id: the role_id
@param firerole_def_ser: the serialized compiled definition
@param firerole_def_src: the sources from which the definition was taken
"""
run_sql("UPDATE accROLE SET firerole_def_ser=%s, firerole_def_src=%s WHERE id=%s", (firerole_def_ser, firerole_def_src, role_id))
def load_role_definition(role_id):
""" Load the definition corresponding to a role. If the compiled definition
is corrupted it try to repairs definitions from their sources and try again
to return the definition.
@param role_id:
@return: a deserialized compiled role definition
"""
res = run_sql("SELECT firerole_def_ser FROM accROLE WHERE id=%s", (role_id, ), 1)
if res:
try:
return deserialize(res[0][0])
except Exception:
## Something bad might have happened? (Update of Python?)
repair_role_definitions()
res = run_sql("SELECT firerole_def_ser FROM accROLE WHERE id=%s", (role_id, ), 1)
if res:
return deserialize(res[0][0])
return CFG_ACC_EMPTY_ROLE_DEFINITION_OBJ
-def acc_firerole_suggest_apache_p(firerole_def_obj):
- """Return True if the given firerole definition suggest the authentication
- through Apache."""
- try:
- default_allow_p, suggest_apache_p, rules = firerole_def_obj
- return suggest_apache_p
- except Exception, msg:
- raise InvenioWebAccessFireroleError, msg
-
def acc_firerole_extract_emails(firerole_def_obj):
"""
Best effort function to extract all the possible email addresses
authorized by the given firerole.
"""
authorized_emails = set()
try:
- default_allow_p, suggest_apache_p, rules = firerole_def_obj
+ default_allow_p, rules = firerole_def_obj
for (allow_p, not_p, field, expressions_list) in rules: # for every rule
if not_p:
continue
if field == 'group':
for reg_p, expr in expressions_list:
if reg_p:
continue
if CFG_CERN_SITE and expr.endswith(' [CERN]'):
authorized_emails.add(expr[:len(' [CERN]')].lower().strip() + '@cern.ch')
emails = run_sql("SELECT user.email FROM usergroup JOIN user_usergroup ON usergroup.id=user_usergroup.id_usergroup JOIN user ON user.id=user_usergroup.id_user WHERE usergroup.name=%s", (expr, ))
for email in emails:
authorized_emails.add(email[0].lower().strip())
elif field == 'email':
for reg_p, expr in expressions_list:
if reg_p:
continue
authorized_emails.add(expr.lower().strip())
elif field == 'uid':
for reg_p, expr in expressions_list:
if reg_p:
continue
email = run_sql("SELECT email FROM user WHERE id=%s", (expr, ))
if email:
authorized_emails.add(email[0][0].lower().strip())
return authorized_emails
except Exception, msg:
raise InvenioWebAccessFireroleError, msg
def acc_firerole_check_user(user_info, firerole_def_obj):
""" Given a user_info dictionary, it matches the rules inside the deserializez
compiled definition in order to discover if the current user match the roles
corresponding to this definition.
@param user_info: a dict produced by collect_user_info which contains every
info about a user
@param firerole_def_obj: a compiled deserialized definition produced by
compile_role_defintion
@return: True if the user match the definition, False otherwise.
"""
try:
- default_allow_p, suggest_apache_p, rules = firerole_def_obj
+ default_allow_p, rules = firerole_def_obj
for (allow_p, not_p, field, expressions_list) in rules: # for every rule
- group_p = field in ('group', 'apache_group') # Is it related to group?
+ group_p = field == 'group' # Is it related to group?
ip_p = field == 'remote_ip' # Is it related to Ips?
until_p = field == 'until' # Is it related to dates?
from_p = field == 'from' # Idem.
next_expr_p = False # Silly flag to break 2 for cycles
if not user_info.has_key(field) and not from_p and not until_p:
continue
for reg_p, expr in expressions_list: # For every element in the rule
if group_p: # Special case: groups
if reg_p: # When it is a regexp
for group in user_info[field]: # iterate over every group
if expr.match(group): # if it matches
if not_p: # if must not match
next_expr_p = True # let's skip to next expr
break
else: # Ok!
return allow_p
if next_expr_p:
break # I said: let's skip to next rule ;-)
elif expr.lower() in [group.lower() for group in user_info[field]]: # Simple expression then just check for expr in groups
if not_p: # If expr is in groups then if must not match
break # let's skip to next expr
else: # Ok!
return allow_p
elif reg_p: # Not a group, then easier. If it's a regexp
if expr.match(user_info[field]): # if it matches
if not_p: # If must not match
break # Let's skip to next expr
else:
return allow_p # Ok!
elif ip_p and type(expr) == type(()): # If it's just a simple expression but an IP!
if _ipmatch(user_info['remote_ip'], expr): # Then if Ip matches
if not_p: # If must not match
break # let's skip to next expr
else:
return allow_p # ok!
elif until_p:
if time.time() <= expr:
if allow_p:
break
else:
return False
elif allow_p:
return False
else:
break
elif from_p:
if time.time() >= expr:
if allow_p:
break
else:
return False
elif allow_p:
return False
else:
break
elif expr.lower() == user_info[field].lower(): # Finally the easiest one!!
if not_p: # ...
break
else: # ...
return allow_p # ...
if not_p and not next_expr_p: # Nothing has matched and we got not
return allow_p # Then the whole rule matched!
except Exception, msg:
raise InvenioWebAccessFireroleError, msg
return default_allow_p # By default we allow ;-) it'an OpenAccess project
def serialize(firerole_def_obj):
""" Serialize and compress a definition."""
if firerole_def_obj == CFG_ACC_EMPTY_ROLE_DEFINITION_OBJ:
return CFG_ACC_EMPTY_ROLE_DEFINITION_SER
elif firerole_def_obj:
return compress(cPickle.dumps(firerole_def_obj, -1))
else:
return CFG_ACC_EMPTY_ROLE_DEFINITION_SER
def deserialize(firerole_def_ser):
""" Deserialize and decompress a definition."""
if firerole_def_ser:
return cPickle.loads(decompress(blob_to_string(firerole_def_ser)))
else:
return CFG_ACC_EMPTY_ROLE_DEFINITION_OBJ
# IMPLEMENTATION
# Comment finder
_no_comment_re = re.compile(r'[\s]*(?allow|deny)[\s]+(?:(?Pnot)[\s]+)?(?P[\w]+)[\s]+(?P(?([\s]*,[\s]*((?allow|deny)[\s]+(any|all)[\s]*', re.I)
# Sub expression finder
_expressions_re = re.compile(r'(? 123.2.12.12
+ """
+ try:
+ return '.'.join(str(int(number)) for number in ip.split('.'))
+ except ValueError:
+ ## e.g. if it's IPV6 ::1
+ return ip
+
+def load_robot_keys():
+ """
+ @return: the robot key dictionary.
+ """
+ from cPickle import loads
+ from zlib import decompress
+ try:
+ robot_keys = loads(decompress(open(CFG_ROBOT_KEYS_PATH).read()))
+ if not isinstance(robot_keys, dict):
+ return {}
+ else:
+ return robot_keys
+ except:
+ return {}
+
+class ExternalAuthRobot(ExternalAuth):
+ """
+ This class implement an external authentication method suitable to be
+ used by an external service that, after having authenticated a user,
+ will provide a URL to the user that, once followed, will successfully
+ login the user into Invenio, with any detail the external service
+ decided to provide to the Invenio installation.
+
+ Such URL should be built as follows:
+ BASE?QUERY
+
+ where BASE is CFG_SITE_SECURE_URL/youraccount/robotlogin
+
+ and QUERY is a urlencoded mapping of the following key->values:
+ - assertion: an assertion, i.e. a piece of information describing the
+ user, see below for more details.
+ - robot: the identifier of the external service providing the assertion
+ - login_method: the name of the login method as defined in CFG_EXTERNAL_AUTHENTICATION.
+ - digest: the digest of the signature as detailed below.
+ - referer: the URL where the user should be redirected after successful
+ login (it is called referer as, for historical reasons, this is the
+ original URL of the page on which, a human-user has clicked "login".
+
+ the "assertion" should be a JSON serialized mapping with the following
+ keys:
+ - email: the email of the user (i.e. its identifier).
+ - nickname: optional nickname of the user.
+ - groups: an optional ';'-separated list of groups to which the user
+ belongs to.
+ - __timeout__: the number of seconds (floating point) from the Epoch,
+ after which the URL will no longer be valid. (expressed in UTC)
+ - __userip__: the IP address of the user for whom this URL has been
+ created. (if the user will follow this URL using a different URL the
+ request will not be valid)
+ - any other key can be added and will be merged in the external user
+ settings.
+
+ If L{use_zlib} is True the assertion is a base64-url-flavour encoding
+ of the zlib compression of the original assertion (useful for shortening
+ the URL while make it easy to type).
+
+ The "digest" is the hexadecimal representation of the digest using the
+ HMAC-SHA1 method to sign the assertion with the secret key associated
+ with the robot for the given login_method.
+
+ @param enforce_external_nicknames: whether to trust nicknames provided by
+ the external service and use them (if possible) as unique identifier
+ in the system.
+ @type enforce_external_nicknames: boolean
+ @param email_attribute_name: the actual key in the assertion that will
+ contain the email.
+ @type email_attribute_name: string
+ @param nickname_attribute_name: the actual key in the assertion that will
+ contain the nickname.
+ @type nickname_attribute_name: string
+ @param groups_attribute_name: the actual key in the assertion that will
+ contain the groups.
+ @type groups_attribute_name: string
+ @param groups_separator: the string used to separate groups.
+ @type groups_separator: string
+ @param timeout_attribute_name: the actual key in the assertion that will
+ contain the timeout.
+ @type timeout_attribute_name: string
+ @param userip_attribute_name: the actual key in the assertion that will
+ contain the user IP.
+ @type userip_attribute_name: string
+ @param check_user_ip: whether to check for the IP address of the user
+ using the given URL, against the IP address stored in the assertion
+ to be identical.
+ @type check_user_ip: boolean
+ @param use_zlib: whether to use base64-url-flavour encoding of the zlib
+ compression of the json serialization of the assertion or simply
+ the json serialization of the assertion.
+ @type use_zlib: boolean
+ """
+ def __init__(self, enforce_external_nicknames=False,
+ email_attribute_name=CFG_ROBOT_EMAIL_ATTRIBUTE_NAME,
+ nickname_attribute_name=CFG_ROBOT_NICKNAME_ATTRIBUTE_NAME,
+ groups_attribute_name=CFG_ROBOT_GROUPS_ATTRIBUTE_NAME,
+ groups_separator=CFG_ROBOT_GROUPS_SEPARATOR,
+ timeout_attribute_name=CFG_ROBOT_TIMEOUT_ATTRIBUTE_NAME,
+ userip_attribute_name=CFG_ROBOT_USERIP_ATTRIBUTE_NAME,
+ check_user_ip=True,
+ use_zlib=True,
+ ):
+ ExternalAuth.__init__(self, enforce_external_nicknames=enforce_external_nicknames)
+ self.email_attribute_name = email_attribute_name
+ self.nickname_attribute_name = nickname_attribute_name
+ self.groups_attribute_name = groups_attribute_name
+ self.groups_separator = groups_separator
+ self.timeout_attribute_name = timeout_attribute_name
+ self.userip_attribute_name = userip_attribute_name
+ self.check_user_ip = check_user_ip
+ self.use_zlib = use_zlib
+
+ def __extract_attribute(self, req):
+ """
+ Load from the request the given assertion, extract all the attribute
+ to properly login the user, and verify that the data are actually
+ both well formed and signed correctly.
+ """
+ from invenio.bibedit_utils import json_unicode_to_utf8
+ from invenio.webinterface_handler import wash_urlargd
+ args = wash_urlargd(req.form, {
+ 'assertion': (str, ''),
+ 'robot': (str, ''),
+ 'digest': (str, ''),
+ 'login_method': (str, '')})
+ assertion = args['assertion']
+ digest = args['digest']
+ robot = args['robot']
+ login_method = args['login_method']
+ shared_key = load_robot_keys().get(login_method, {}).get(robot)
+ if shared_key is None:
+ raise InvenioWebAccessExternalAuthError("A key does not exist for robot: %s, login_method: %s" % (robot, login_method))
+ if not self.verify(shared_key, assertion, digest):
+ raise InvenioWebAccessExternalAuthError("The provided assertion does not validate against the digest %s for robot %s" % (repr(digest), repr(robot)))
+ if self.use_zlib:
+ try:
+ assertion = decompress(base64.urlsafe_b64decode(assertion))
+ except:
+ raise InvenioWebAccessExternalAuthError("The provided assertion is corrupted")
+ data = json_unicode_to_utf8(json.loads(assertion))
+ if not isinstance(data, dict):
+ raise InvenioWebAccessExternalAuthError("The provided assertion is invalid")
+ timeout = data[self.timeout_attribute_name]
+ if timeout < time.time():
+ raise InvenioWebAccessExternalAuthError("The provided assertion is expired")
+ userip = data.get(self.userip_attribute_name)
+ if not self.check_user_ip or (normalize_ip(userip) == normalize_ip(req.remote_ip)):
+ return data
+ else:
+ raise InvenioWebAccessExternalAuthError("The provided assertion has been issued for a different IP address (%s instead of %s)" % (userip, req.remote_ip))
+
+ def auth_user(self, username, password, req=None):
+ """Authenticate user-supplied USERNAME and PASSWORD. Return
+ None if authentication failed, or the email address of the
+ person if the authentication was successful. In order to do
+ this you may perhaps have to keep a translation table between
+ usernames and email addresses.
+ Raise InvenioWebAccessExternalAuthError in case of external troubles.
+ """
+ data = self.__extract_attribute(req)
+ email = data.get(self.email_attribute_name)
+ if email:
+ if isinstance(email, str):
+ return email.strip().lower()
+ else:
+ raise InvenioWebAccessExternalAuthError("The email provided in the assertion is invalid: %s" % (repr(email)))
+ else:
+ return None
+
+ def fetch_user_groups_membership(self, username, password=None, req=None):
+ """Given a username and a password, returns a dictionary of groups
+ and their description to which the user is subscribed.
+ Raise InvenioWebAccessExternalAuthError in case of troubles.
+ """
+ if self.groups_attribute_name:
+ data = self.__extract_attribute(req)
+ groups = data.get(self.groups_attribute_name)
+ if groups:
+ if isinstance(groups, str):
+ groups = [group.strip() for group in groups.split(self.groups_separator)]
+ return dict(zip(groups, groups))
+ else:
+ raise InvenioWebAccessExternalAuthError("The groups provided in the assertion are invalid: %s" % (repr(groups)))
+ return {}
+
+ def fetch_user_nickname(self, username, password=None, req=None):
+ """Given a username and a password, returns the right nickname belonging
+ to that user (username could be an email).
+ """
+ if self.nickname_attribute_name:
+ data = self.__extract_attribute(req)
+ nickname = data.get(self.nickname_attribute_name)
+ if nickname:
+ if isinstance(nickname, str):
+ return nickname.strip().lower()
+ else:
+ raise InvenioWebAccessExternalAuthError("The nickname provided in the assertion is invalid: %s" % (repr(nickname)))
+ return None
+
+ def fetch_user_preferences(self, username, password=None, req=None):
+ """Given a username and a password, returns a dictionary of keys and
+ values, corresponding to external infos and settings.
+
+ userprefs = {"telephone": "2392489",
+ "address": "10th Downing Street"}
+
+ (WEBUSER WILL erase all prefs that starts by EXTERNAL_ and will
+ store: "EXTERNAL_telephone"; all internal preferences can use whatever
+ name but starting with EXTERNAL). If a pref begins with HIDDEN_ it will
+ be ignored.
+ """
+ data = self.__extract_attribute(req)
+ for key in (self.email_attribute_name, self.groups_attribute_name, self.nickname_attribute_name, self.timeout_attribute_name, self.userip_attribute_name):
+ if key and key in data:
+ del data[key]
+ return data
+
+ def robot_login_method_p():
+ """Return True if this method is dedicated to robots and should
+ not therefore be available as a choice to regular users upon login.
+ """
+ return True
+ robot_login_method_p = staticmethod(robot_login_method_p)
+
+ def sign(secret, assertion):
+ """
+ @return: a signature of the given assertion.
+ @rtype: string
+ @note: override this method if you want to change the signature
+ algorithm (e.g. to use GPG).
+ @see: L{verify}
+ """
+ return hmac.new(secret, assertion, sha1).hexdigest()
+ sign = staticmethod(sign)
+
+ def verify(secret, assertion, signature):
+ """
+ @return: True if the signature is valid
+ @rtype: boolean
+ @note: override this method if you want to change the signature
+ algorithm (e.g. to use GPG)
+ @see: L{sign}
+ """
+ return hmac.new(secret, assertion, sha1).hexdigest() == signature
+ verify = staticmethod(verify)
+
+ def test_create_example_url(self, email, login_method, robot, ip, assertion=None, timeout=None, referer=None, groups=None, nickname=None):
+ """
+ Create a test URL to test the robot login.
+
+ @param email: email of the user we want to login as.
+ @type email: string
+ @param login_method: the login_method name as specified in CFG_EXTERNAL_AUTHENTICATION.
+ @type login_method: string
+ @param robot: the identifier of this robot.
+ @type robot: string
+ @param assertion: any further data we want to send to.
+ @type: json serializable mapping
+ @param ip: the IP of the user.
+ @type: string
+ @param timeout: timeout when the URL will expire (in seconds from the Epoch)
+ @type timeout: float
+ @param referer: the URL where to land after successful login.
+ @type referer: string
+ @param groups: the list of optional group of the user.
+ @type groups: list of string
+ @param nickname: the optional nickname of the user.
+ @type nickname: string
+ @return: the URL to login as the user.
+ @rtype: string
+ """
+ from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION
+ from invenio.urlutils import create_url
+ if assertion is None:
+ assertion = {}
+ assertion[self.email_attribute_name] = email
+ if nickname:
+ assertion[self.nickname_attribute_name] = nickname
+ if groups:
+ assertion[self.groups_attribute_name] = self.groups_separator.join(groups)
+ if timeout is None:
+ timeout = time.time() + CFG_ROBOT_URL_TIMEOUT
+ assertion[self.timeout_attribute_name] = timeout
+ if referer is None:
+ referer = CFG_SITE_URL
+ if login_method is None:
+ for a_login_method, details in CFG_EXTERNAL_AUTHENTICATION.iteritems():
+ if details[2]:
+ login_method = a_login_method
+ break
+ robot_keys = load_robot_keys()
+ assertion[self.userip_attribute_name] = ip
+ assertion = json.dumps(assertion)
+ if self.use_zlib:
+ assertion = base64.urlsafe_b64encode(compress(assertion))
+ shared_key = robot_keys[login_method][robot]
+ digest = self.sign(shared_key, assertion)
+ return create_url("%s%s" % (CFG_SITE_SECURE_URL, "/youraccount/robotlogin"), {
+ 'assertion': assertion,
+ 'robot': robot,
+ 'login_method': login_method,
+ 'digest': digest,
+ 'referer': referer})
+
+def update_robot_key(login_method, robot, key=None):
+ """
+ Utility to update the robot key store.
+ @param login_method: the login_method name as per L{CFG_EXTERNAL_AUTHENTICATION}.
+ It should correspond to a robot-enable login method.
+ @type: string
+ @param robot: the robot identifier
+ @type robot: string
+ @param key: the secret
+ @type key: string
+ @note: if the secret is empty the corresponding key will be removed.
+ """
+ from invenio.websearch_webcoll import mymkdir
+ robot_keys = load_robot_keys()
+ if key is None and login_method in robot_keys and robot in robot_keys[login_method]:
+ del robot_keys[login_method][robot]
+ if not robot_keys[login_method]:
+ del robot_keys[login_method]
+ else:
+ if login_method not in robot_keys:
+ robot_keys[login_method] = {}
+ robot_keys[login_method][robot] = key
+ mymkdir(os.path.join(CFG_ETCDIR, 'webaccess'))
+ open(CFG_ROBOT_KEYS_PATH, 'w').write(compress(dumps(robot_keys, -1)))
+
diff --git a/modules/webaccess/lib/webaccess_regression_tests.py b/modules/webaccess/lib/webaccess_regression_tests.py
index 8b0663efd..83f05aade 100644
--- a/modules/webaccess/lib/webaccess_regression_tests.py
+++ b/modules/webaccess/lib/webaccess_regression_tests.py
@@ -1,111 +1,241 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebAccess Regression Test Suite."""
__revision__ = "$Id$"
import unittest
+import socket
+import time
+import cgi
+
+from urlparse import urlparse, urlunparse
+from urllib import urlopen, urlencode
from invenio.access_control_admin import acc_add_role, acc_delete_role, \
acc_get_role_definition
from invenio.access_control_firerole import compile_role_definition, \
serialize, deserialize
-from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL
+from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_DEVEL_SITE
from invenio.testutils import make_test_suite, run_test_suite, \
test_web_page_content, merge_error_messages
class WebAccessWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebAccess web pages whether they are up or not."""
def test_webaccess_admin_interface_availability(self):
"""webaccess - availability of WebAccess Admin interface pages"""
baseurl = CFG_SITE_URL + '/admin/webaccess/webaccessadmin.py/'
_exports = ['', 'delegate_startarea', 'manageaccounts']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_webaccess_admin_guide_availability(self):
"""webaccess - availability of WebAccess Admin guide pages"""
url = CFG_SITE_URL + '/help/admin/webaccess-admin-guide'
error_messages = test_web_page_content(url,
expected_text="WebAccess Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
class WebAccessFireRoleTest(unittest.TestCase):
"""Check WebAccess behaviour WRT FireRole."""
def setUp(self):
"""Create a fake role."""
self.role_name = 'test'
self.role_description = 'test role'
self.role_definition = 'allow email /.*@cern.ch/'
self.role_id, dummy, dummy, dummy = acc_add_role(self.role_name,
self.role_description,
serialize(compile_role_definition(self.role_definition)),
self.role_definition)
def tearDown(self):
"""Drop the fake role."""
acc_delete_role(self.role_id)
def test_webaccess_firerole_serialization(self):
"""webaccess - firerole role definition correctly serialized"""
def_ser = compile_role_definition(self.role_definition)
tmp_def_ser = acc_get_role_definition(self.role_id)
self.assertEqual(def_ser, deserialize(tmp_def_ser))
class WebAccessUseBasketsTest(unittest.TestCase):
"""
Check WebAccess behaviour WRT enabling/disabling web modules such
as baskets.
"""
def test_precached_area_authorization(self):
"""webaccess - login-time precached authorizations for usebaskets"""
error_messages = test_web_page_content(CFG_SITE_SECURE_URL + '/youraccount/display?ln=en', username='jekyll', password='j123ekyll', expected_text='Your Baskets')
error_messages.extend(test_web_page_content(CFG_SITE_SECURE_URL + '/youraccount/display?ln=en', username='hyde', password='h123yde', unexpected_text='Your Baskets'))
if error_messages:
self.fail(merge_error_messages(error_messages))
-
-TEST_SUITE = make_test_suite(WebAccessWebPagesAvailabilityTest,
- WebAccessFireRoleTest,
- WebAccessUseBasketsTest)
+if CFG_DEVEL_SITE:
+ class WebAccessRobotLoginTest(unittest.TestCase):
+ """
+ Check whether robot login functionality is OK.
+ """
+ def _erase_example_user_and_groups(self):
+ from invenio.dbquery import run_sql
+ uid = run_sql("SELECT id FROM user WHERE email=%s", (self.a_email, ))
+ if uid:
+ run_sql("DELETE FROM user WHERE id=%s", (uid[0][0], ))
+ run_sql("DELETE FROM user_usergroup WHERE id_user=%s", (uid[0][0], ))
+ for method_name in self.robot_login_methods:
+ for group in self.some_groups:
+ run_sql("DELETE FROM usergroup WHERE name=%s", ("%s [%s]" % (group, method_name), ))
+
+ def setUp(self):
+ from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION
+ self.robot_login_methods = dict([(method_name, CFG_EXTERNAL_AUTHENTICATION[method_name]) for method_name in CFG_EXTERNAL_AUTHENTICATION if CFG_EXTERNAL_AUTHENTICATION[method_name] and CFG_EXTERNAL_AUTHENTICATION[method_name].robot_login_method_p()])
+ self.a_robot = "regression-test"
+ self.a_password = "123"
+ self.a_email = "foo.bar@example.org"
+ self.a_nickname = "foo-bar"
+ self.some_groups = ["a group for regression test", "another group for regression test"]
+ self.myip = urlopen(CFG_SITE_URL + "/httptest/whatismyip").read()
+ from invenio.external_authentication_robot import update_robot_key
+ for method_name in self.robot_login_methods:
+ update_robot_key(method_name, self.a_robot, self.a_password)
+ from invenio.external_authentication_robot import load_robot_keys
+
+ def tearDown(self):
+ from invenio.external_authentication_robot import update_robot_key
+ #for method_name in self.robot_login_methods:
+ #update_robot_key(method_name, self.a_robot)
+ from invenio.external_authentication_robot import load_robot_keys
+ self._erase_example_user_and_groups()
+
+ def test_normal_robot_login_method(self):
+ """webaccess - robot login method"""
+ for method_name, method in self.robot_login_methods.iteritems():
+ url = method.test_create_example_url(self.a_email, method_name, self.a_robot, self.myip)
+ try:
+ error_messages = test_web_page_content(url, expected_text=self.a_email)
+ if error_messages:
+ self.fail(merge_error_messages(error_messages))
+ finally:
+ self._erase_example_user_and_groups()
+
+ def test_robot_login_method_with_nickname(self):
+ """webaccess - robot login method with nickname"""
+ for method_name, method in self.robot_login_methods.iteritems():
+ if method.enforce_external_nicknames:
+ url = method.test_create_example_url(self.a_email, method_name, self.a_robot, self.myip, nickname=self.a_nickname)
+ try:
+ error_messages = test_web_page_content(url, expected_text=self.a_nickname)
+ if error_messages:
+ self.fail(merge_error_messages(error_messages))
+ finally:
+ self._erase_example_user_and_groups()
+
+ def test_robot_login_method_with_groups(self):
+ """webaccess - robot login method with groups"""
+ for method_name, method in self.robot_login_methods.iteritems():
+ url = method.test_create_example_url(self.a_email, method_name, self.a_robot, self.myip, groups=self.some_groups, referer=CFG_SITE_SECURE_URL + "/yourgroups/display")
+ try:
+ for group in self.some_groups:
+ error_messages = test_web_page_content(url, expected_text="%s [%s]" % (group, method_name))
+ if error_messages:
+ self.fail(merge_error_messages(error_messages))
+ finally:
+ self._erase_example_user_and_groups()
+
+ def test_robot_login_method_wrong_ip(self):
+ """webaccess - robot login method wrong IP"""
+ for method_name, method in self.robot_login_methods.iteritems():
+ url = method.test_create_example_url(self.a_email, method_name, self.a_robot, '123.123.123.123')
+ try:
+ error_messages = test_web_page_content(url, expected_text="The provided assertion has been issued for a different IP address")
+ if error_messages:
+ self.fail(merge_error_messages(error_messages))
+ finally:
+ self._erase_example_user_and_groups()
+
+ def test_robot_login_method_expired_assertion(self):
+ """webaccess - robot login method with expired assertion"""
+ for method_name, method in self.robot_login_methods.iteritems():
+ url = method.test_create_example_url(self.a_email, method_name, self.a_robot, self.myip, timeout=time.time())
+ time.sleep(1)
+ try:
+ error_messages = test_web_page_content(url, expected_text="The provided assertion is expired")
+ if error_messages:
+ self.fail(merge_error_messages(error_messages))
+ finally:
+ self._erase_example_user_and_groups()
+
+ def test_robot_login_method_with_invalid_signature(self):
+ """webaccess - robot login method with invalid signature"""
+ for method_name, method in self.robot_login_methods.iteritems():
+ url = method.test_create_example_url(self.a_email, method_name, self.a_robot, self.myip)
+ url = list(urlparse(url))
+ query = cgi.parse_qs(url[4])
+ for key, value in query.items():
+ query[key] = value[0]
+ digest = query['digest']
+ digest0 = digest[0]
+ if digest0 == '0':
+ digest0 = '1'
+ else:
+ digest0 = '0'
+ digest = digest0 + digest[1:]
+ query['digest'] = digest
+ url[4] = urlencode(query)
+ url = urlunparse(url)
+ try:
+ error_messages = test_web_page_content(url, expected_text="does not validate against the digest")
+ if error_messages:
+ self.fail(merge_error_messages(error_messages))
+ finally:
+ self._erase_example_user_and_groups()
+
+
+ TEST_SUITE = make_test_suite(WebAccessWebPagesAvailabilityTest,
+ WebAccessFireRoleTest,
+ WebAccessUseBasketsTest,
+ WebAccessRobotLoginTest)
+else:
+ TEST_SUITE = make_test_suite(WebAccessWebPagesAvailabilityTest,
+ WebAccessFireRoleTest,
+ WebAccessUseBasketsTest)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/webaccess/lib/webaccessadmin_lib.py b/modules/webaccess/lib/webaccessadmin_lib.py
index 77a8d6fbb..5ffb74b3c 100644
--- a/modules/webaccess/lib/webaccessadmin_lib.py
+++ b/modules/webaccess/lib/webaccessadmin_lib.py
@@ -1,3648 +1,3837 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio WebAccess Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
## fill config variables:
import re
import random
import getopt
import sys
+import time
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_ACCESS_CONTROL_LEVEL_GUESTS, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN, \
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_ADMIN_EMAIL, \
- CFG_SITE_URL
+ CFG_SITE_URL, \
+ CFG_SITE_SECURE_URL
import invenio.access_control_engine as acce
import invenio.access_control_admin as acca
from invenio.mailutils import send_email
from invenio.bibrankadminlib import addadminbox, tupletotable, \
tupletotable_onlyselected, addcheckboxes, createhiddenform
from invenio.access_control_firerole import compile_role_definition, \
repair_role_definitions, serialize
from invenio.messages import gettext_set_language
from invenio.dbquery import run_sql, OperationalError
from invenio.webpage import page
-from invenio.webuser import getUid, isGuestUser, page_not_authorized
+from invenio.webuser import getUid, isGuestUser, page_not_authorized, collect_user_info
from invenio.webuser import email_valid_p, get_user_preferences, \
set_user_preferences, update_Uid
-from invenio.urlutils import redirect_to_url
+from invenio.urlutils import redirect_to_url, wash_url_argument
from invenio.access_control_config import DEF_DEMO_USER_ROLES, \
DEF_DEMO_ROLES, DEF_DEMO_AUTHS, WEBACCESSACTION, MAXPAGEUSERS, \
SUPERADMINROLE, CFG_EXTERNAL_AUTHENTICATION, DELEGATEADDUSERROLE, \
CFG_ACC_EMPTY_ROLE_DEFINITION_SRC, InvenioWebAccessFireroleError, \
- MAXSELECTUSERS
+ MAXSELECTUSERS, CFG_EXTERNAL_AUTH_DEFAULT
from invenio.bibtask import authenticate
from cgi import escape
+## The following variable is True if the installation make any difference
+## between HTTP Vs. HTTPS connections.
+CFG_HAS_HTTPS_SUPPORT = CFG_SITE_URL != CFG_SITE_SECURE_URL
def index(req, title='', body='', subtitle='', adminarea=2, authorized=0, ln=CFG_SITE_LANG):
"""main function to show pages for webaccessadmin.
1. if user not logged in and administrator, show the mustlogin page
2. if used without body argument, show the startpage
3. show admin page with title, body, subtitle and navtrail.
- adminarea - number codes that tell what extra info to put in the navtrail
- 0 - nothing extra
- 1 - add Delegate Rights
- 2 - add Manage WebAccess
- maybe add:
- 3: role admin
- 4: action admin
- 5: user area
- 6: reset area
-
authorized - if 1, don't check if the user is allowed to be webadmin """
+ if CFG_HAS_HTTPS_SUPPORT and not req.is_https():
+ redirect_to_url(req, "%s/admin/webaccess/webaccessadmin.py" % CFG_SITE_SECURE_URL)
navtrail_previous_links = 'Admin Area' \
- '' % (CFG_SITE_URL,)
+ '' % (CFG_SITE_SECURE_URL,)
if body:
if adminarea == 1:
navtrail_previous_links += '> ' \
- 'Delegate Rights ' % (CFG_SITE_URL, )
+ 'Delegate Rights ' % (CFG_SITE_SECURE_URL, )
if adminarea >= 2 and adminarea < 7:
navtrail_previous_links += '> ' \
'' \
- 'WebAccess Admin ' % (CFG_SITE_URL, )
+ 'WebAccess Admin ' % (CFG_SITE_SECURE_URL, )
if adminarea == 3:
navtrail_previous_links += '> ' \
- 'Role Administration ' % (CFG_SITE_URL, )
+ 'Role Administration ' % (CFG_SITE_SECURE_URL, )
elif adminarea == 4:
navtrail_previous_links += '> ' \
'Action Administration ' % (CFG_SITE_URL, )
+ '/actionarea>Action Administration ' % (CFG_SITE_SECURE_URL, )
elif adminarea == 5:
navtrail_previous_links += '> ' \
'User Administration ' % (CFG_SITE_URL, )
+ '/userarea>User Administration ' % (CFG_SITE_SECURE_URL, )
elif adminarea == 6:
navtrail_previous_links += '> ' \
'Reset Authorizations ' % (CFG_SITE_URL, )
+ '/resetarea>Reset Authorizations ' % (CFG_SITE_SECURE_URL, )
elif adminarea == 7:
navtrail_previous_links += '> ' \
'Manage Accounts ' % (CFG_SITE_URL, )
+ '/manageaccounts>Manage Accounts ' % (CFG_SITE_SECURE_URL, )
elif adminarea == 8:
navtrail_previous_links += '> ' \
'List Groups ' % (CFG_SITE_URL, )
+ '/listgroups>List Groups ' % (CFG_SITE_SECURE_URL, )
+ elif adminarea == 9:
+ navtrail_previous_links += '> ' \
+ 'Manage Robot Login ' % (CFG_SITE_SECURE_URL, )
id_user = getUid(req)
(auth_code, auth_message) = is_adminuser(req)
if not authorized and auth_code != 0:
return mustloginpage(req, auth_message)
elif not body:
title = 'WebAccess Admin'
body = startpage()
elif type(body) != str: body = addadminbox(subtitle, datalist=body)
return page(title=title,
uid=id_user,
req=req,
body=body,
navtrail=navtrail_previous_links,
lastupdated=__lastupdated__)
def mustloginpage(req, message):
"""show a page asking the user to login."""
navtrail_previous_links = '' \
'Admin Area > ' \
- 'WebAccess Admin ' % (CFG_SITE_URL, CFG_SITE_URL)
+ 'WebAccess Admin ' % (CFG_SITE_SECURE_URL, CFG_SITE_SECURE_URL)
return page_not_authorized(req=req, text=message,
navtrail=navtrail_previous_links)
def is_adminuser(req):
"""check if user is a registered administrator. """
return acce.acc_authorize_action(req, WEBACCESSACTION)
+def perform_managerobotlogin(req, robot_name='', new_pwd1='', new_pwd2='', login_method='', timeout='', referer='', ip='', action='', confirm=0, email='', groups='', nickname='', json_assertion='', url_only=0):
+ robot_name = wash_url_argument(robot_name, 'str')
+ new_pwd1 = wash_url_argument(new_pwd1, 'str')
+ new_pwd2 = wash_url_argument(new_pwd2, 'str')
+ login_method = wash_url_argument(login_method, 'str')
+ timeout = wash_url_argument(timeout, 'int')
+ referer = wash_url_argument(referer, 'str')
+ ip = wash_url_argument(ip, 'str')
+ action = wash_url_argument(action, 'str')
+ confirm = wash_url_argument(confirm, 'int')
+ email = wash_url_argument(email, 'str')
+ groups = wash_url_argument(groups, 'str')
+ nickname = wash_url_argument(nickname, 'str')
+ url_only = wash_url_argument(url_only, 'int')
+ json_assertion = wash_url_argument(json_assertion, 'str')
+ from invenio.external_authentication_robot import update_robot_key, load_robot_keys, json
+ (auth_code, auth_message) = acce.acc_authorize_action(req, 'cfgrobotkeys', login_method='*', robot='*')
+ if auth_code != 0: return mustloginpage(req, auth_message)
+
+ available_robot_login_methods = [name for (name, method) in CFG_EXTERNAL_AUTHENTICATION.iteritems() if method and method.robot_login_method_p()]
+
+ errors = []
+ warnings = []
+ messages = []
+ if not available_robot_login_methods:
+ errors.append("""
+You should enable at least on robot based login method in access_control_config.py in the variable CFG_EXTERNAL_AUTHENTICATION.
+""")
+ forms = ""
+ else:
+ robot_keys = load_robot_keys()
+ if not login_method:
+ login_method = available_robot_login_methods[0]
+ if not timeout:
+ timeout = 60 * 60
+ if not ip:
+ ip = req.remote_ip
+ user_info = collect_user_info(req)
+ if not email:
+ email = user_info['email']
+ if not nickname:
+ nickname = user_info['nickname']
+ if not robot_name:
+ if login_method in robot_keys and robot_keys[login_method]:
+ robot_name = robot_keys[login_method].keys()[0]
+ if not referer:
+ referer = CFG_SITE_SECURE_URL
+ if action == 'changepwd':
+ if acce.acc_authorize_action(user_info, 'cfgrobotkeys', login_method=login_method, robot=robot_name)[0]:
+ errors.append("""You don't have proper authorization to modify robot %s for login_method %s.""" % (escape(robot_name), escape(login_method)))
+ if login_method not in available_robot_login_methods:
+ errors.append("""The login method must be one among the available_robot_login_methods (%s).""" % escape(', '.join(available_robot_login_methods)))
+ if new_pwd1 != new_pwd2:
+ errors.append("""The two passwords are not equal.""")
+ new_pwd1 = ''
+ new_pwd2 = ''
+ if not robot_name:
+ errors.append("""The robot name must be specified.""")
+ if int(confirm) == 1:
+ if not errors:
+ update_robot_key(login_method, robot_name, new_pwd1)
+ robot_keys = load_robot_keys()
+ if new_pwd1:
+ messages.append("""The password for robot %s has been successfully updated.""" % escape(robot_name))
+ else:
+ messages.append("""The password for robot %s has been erased, and hence the robot %s does not exist anymore.""" % (escape(robot_name), escape(robot_name)))
+ action = ''
+ confirm = 0
+ robot_name = ''
+ new_pwd1 = ''
+ new_pwd2 = ''
+
+ else:
+ if not new_pwd1:
+ warnings.append("""By setting an empty password you will actually erase the robot %s""" % escape(robot_name))
+ elif action == 'createurl':
+ if acce.acc_authorize_action(user_info, 'cfgrobotkeys', login_method=login_method, robot=robot_name)[0]:
+ errors.append("""You don't have proper authorization to create a URL for robot %s for login_method %s.""" % (escape(robot_name), escape(login_method)))
+ if login_method not in available_robot_login_methods:
+ errors.append("""The login method must be one among the available_robot_login_methods (%s).""" % escape(', '.join(available_robot_login_methods)))
+ if robot_name not in robot_keys.get(login_method, {}):
+ errors.append("""The robot name does not correspond to a valid robot name (for %s these are: %s).""" % (escape(login_method), escape(', '.join(robot_keys.get(login_method, {}).keys()))))
+ if json_assertion.strip():
+ try:
+ assertion = json.loads(json_assertion)
+ assert(isinstance(assertion, dict))
+ except Exception, err:
+ errors.append("""The assertion is not a valid json serializable mapping: %s""" % (err))
+ else:
+ assertion = None
+ if not email:
+ errors.append("""The email is mandatory.""")
+ if not ip:
+ errors.append("""The IP address is mandatory.""")
+ if not errors:
+ url = CFG_EXTERNAL_AUTHENTICATION[login_method].test_create_example_url(email, login_method=login_method, robot=robot_name, ip=ip, timeout=time.time() + timeout, referer=referer, groups=groups.splitlines(), nickname=nickname, assertion=assertion)
+ if url_only:
+ req.content_type = 'text/plain'
+ return url
+ messages.append("""The corresponding URL is: %(url)s""" % {
+ 'url_escape': escape(url, True),
+ 'url': escape(url)
+ })
+ action = ''
+ forms = """
Existing login_method:
%s
""" % ''.join(["
%s (robots: %s)
" % (method, ', '.join(robot_keys.get(login_method, {}))) for method in available_robot_login_methods])
+ forms += """
Existing robot names (for login_method %s):
%s
""" % (escape(login_method), ''.join(["
%s
" % name for name in robot_keys.get(login_method, {})]))
+ confirm_field = """"""
+ if action == 'changepwd':
+ confirm_field = """
+ Please confirm once more you want to change this password."""
+ login_method_boxes = ""
+ for login_method_name in available_robot_login_methods:
+ if login_method_name == login_method:
+ login_method_boxes += """ """ % {'name': escape(login_method_name, True)}
+ else:
+ login_method_boxes += """ """ % {'name': escape(login_method_name, True)}
+
+ forms += """
"""
return index(req=req,
title='Group list',
subtitle='All the groups registered in the system',
body=[output, extra],
adminarea=2)
def perform_rolearea(req, grep=""):
"""create the role area menu page."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
header = ['id', 'name', 'description', 'firewall like role definition',
'users', 'authorizations / actions', 'role', '']
roles = acca.acc_get_all_roles()
roles2 = []
if grep:
try:
re_grep = re.compile(grep)
except Exception, err:
re_grep = None
grep = ''
else:
re_grep = None
for (id, name, desc, dummy, firerole_def_src) in roles:
if not firerole_def_src:
firerole_def_src = '' ## Workaround for None.
if re_grep and not re_grep.search(name) and not re_grep.search(desc) and not re_grep.search(firerole_def_src):
## We're grepping for some word.
## Let's dig into the authorization then.
all_actions = acca.acc_find_possible_actions_all(id)
## FIXME: the acc_find_possible_actions_all is really an ugly
## function, but is the closest to what it's needed in order
## to retrieve all the authorization of a role.
for idx, row in enumerate(all_actions):
grepped = False
if idx % 2 == 0:
## even lines contains headers like in:
## ['role', 'action', '#', 'collection']
## the only useful text to grep is from index 3 onwards
for keyword in row[3:]:
if re_grep.search(keyword):
grepped = True
break
if grepped:
break
else:
## odd lines contains content like in:
## [1, 18L, 1, 'Theses']
## the useful text to grep is indirectly index 1
## which is indeed the id_action (needed to retrieve the
## action name) and from column 3 onwards.
if re_grep.search(acca.acc_get_action_name(row[1])):
break
for value in row[3:]:
if re_grep.search(value):
grepped = True
break
if grepped:
break
else:
## We haven't grepped anything!
## Let's skip to the next role then...
continue
if len(desc) > 30:
desc = desc[:30] + '...'
if firerole_def_src and len(firerole_def_src) > 30:
firerole_def_src = firerole_def_src[:30] + '...'
roles2.append([id, name, desc, firerole_def_src])
for col in [(('add', 'adduserrole'),
('delete', 'deleteuserrole'),),
(('add', 'addauthorization'),
('modify', 'modifyauthorizations'),
('remove', 'deleteroleaction')),
(('modify', 'modifyrole'),
('delete', 'deleterole')),
(('show details', 'showroledetails'), )]:
roles2[-1].append('%s' %
(col[0][1], id, col[0][0]))
for (str, function) in col[1:]:
roles2[-1][-1] += ' / %s' % \
(function, id, str)
output = """
Users:
add or remove users from the access to a role and its priviliges.
Authorizations/Actions:
these terms means almost the same, but an authorization is a
connection between a role and an action (possibly) containing arguments.
Roles:
see all the information attached to a role and decide if you want
to delete it.
""" % escape(grep)
output += tupletotable(header=header, tuple=roles2)
extra = """
"""
return index(req=req,
title='Role Administration',
subtitle='administration with roles as access point',
body=[output, extra],
adminarea=2)
def perform_actionarea(req, grep=''):
"""create the action area menu page."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
if grep:
try:
re_grep = re.compile(grep)
except Exception, err:
re_grep = None
grep = ''
else:
re_grep = None
header = ['name', 'authorizations/roles', '']
actions = acca.acc_get_all_actions()
actions2 = []
roles2 = []
for (id, name, description) in actions:
if re_grep and not re_grep.search(name) and not re_grep.search(description):
grepped = False
roles = acca.acc_get_action_roles(id)
for id_role, role_name, role_description in roles:
if re_grep.search(role_name) or re_grep.search(role_description):
grepped = True
break
elif re_grep.search(acca.acc_get_role_details(id_role)[3] or ''):
## Found in FireRole
grepped = True
break
else:
details = acca.acc_find_possible_actions(id_role, id)
if details:
for argument in details[0][1:]:
if re_grep.search(argument):
grepped = True
break
for values in details[1:]:
for value in values[1:]:
if re_grep.search(value):
grepped = True
break
if grepped:
break
if grepped:
break
if not grepped:
continue
actions2.append([name, description])
for col in [(('add', 'addauthorization'),
('modify', 'modifyauthorizations'),
('remove', 'deleteroleaction')),
(('show details', 'showactiondetails'), )]:
actions2[-1].append('%s'
'' % (col[0][1], id, col[0][0]))
for (str, function) in col[1:]:
actions2[-1][-1] += ' / %s' % (function, id, str)
output = """
Authorizations/Roles:
these terms means almost the same, but an authorization is a
connection between a role and an action (possibly) containing
arguments.
Actions:
see all the information attached to an action.
""" % escape(grep)
output += tupletotable(header=header, tuple=actions2)
extra = """
"""
return index(req=req,
title='Action Administration',
subtitle='administration with actions as access point',
body=[output, extra],
adminarea=2)
def perform_userarea(req, email_user_pattern=''):
"""create area to show info about users. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
subtitle = 'step 1 - search for users'
output = """
search for users to display.
"""
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' 1. search for user\n'
text += ' \n' % (email_user_pattern, )
output += createhiddenform(action="userarea",
text=text,
button="search for users")
if email_user_pattern:
try:
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s
ORDER BY email LIMIT %s""", (email_user_pattern, MAXPAGEUSERS+1))
except OperationalError:
users1 = ()
if not users1:
output += '
no matching users
'
else:
subtitle = 'step 2 - select what to do with user'
users = []
for (id, email) in users1[:MAXPAGEUSERS]:
users.append([id, email])
for col in [(('add', 'addroleuser'),
('remove', 'deleteuserrole')),
(('show details', 'showuserdetails'), )]:
users[-1].append('%s' % (col[0][1],
email_user_pattern, id, col[0][0]))
for (str, function) in col[1:]:
users[-1][-1] += ' / %s' % \
(function, email_user_pattern, id, str)
output += '
keep all changes and add the default authorization settings.
"""
return index(req=req,
title='Reset Authorizations',
subtitle='reseting to or adding default authorizations',
body=[output],
adminarea=2)
def perform_resetdefaultsettings(req, superusers=[], confirm=0):
"""delete all roles, actions and authorizations presently in the database
and add only the default roles.
only selected users will be added to superadmin, rest is blank """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
# cleaning input
if type(superusers) == str: superusers = [superusers]
# remove not valid e-mails
for email in superusers:
if not check_email(email): superusers.remove(email)
# instructions
output = """
before you reset the settings, we need some users
to connect to %s.
enter as many e-mail addresses you want and press reset. confirm reset settings when you have added enough e-mails. %s is added as default.
'
output += tupletotable(header=['e-mail address'],
tuple=superusers,
start=start,
extracolumn=extra,
end=end)
if confirm in [1, "1"]:
res = acca.acc_reset_default_settings(superusers)
if res:
output += '
successfully reset default settings
'
else:
output += '
sorry, could not reset default settings
'
return index(req=req,
title='Reset Default Settings',
subtitle='reset settings',
body=[output],
adminarea=6)
def perform_adddefaultsettings(req, superusers=[], confirm=0):
"""add the default settings, and keep everything else.
probably nothing will be deleted, except if there has been made changes to the defaults."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
# cleaning input
if type(superusers) == str: superusers = [superusers]
# remove not valid e-mails
for email in superusers:
if not check_email(email): superusers.remove(email)
# instructions
output = """
before you add the settings, we need some users
to connect to %s.
enter as many e-mail addresses you want and press add. confirm add settings when you have added enough e-mails. %s is added as default.
- """ % (CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL)
+ """ % (CFG_SITE_SECURE_URL, CFG_SITE_SECURE_URL, CFG_SITE_SECURE_URL, CFG_SITE_SECURE_URL, CFG_SITE_SECURE_URL)
if mtype == "perform_accesspolicy" and content:
fin_output += content
elif mtype == "perform_accesspolicy" or mtype == "perform_showall":
fin_output += perform_accesspolicy(req, callback='')
fin_output += " "
if mtype == "perform_accountoverview" and content:
fin_output += content
elif mtype == "perform_accountoverview" or mtype == "perform_showall":
fin_output += perform_accountoverview(req, callback='')
fin_output += " "
if mtype == "perform_createaccount" and content:
fin_output += content
elif mtype == "perform_createaccount" or mtype == "perform_showall":
fin_output += perform_createaccount(req, callback='')
fin_output += " "
if mtype == "perform_modifyaccounts" and content:
fin_output += content
elif mtype == "perform_modifyaccounts" or mtype == "perform_showall":
fin_output += perform_modifyaccounts(req, callback='')
fin_output += " "
if mtype == "perform_becomeuser" and content:
fin_output += content
elif mtype == "perform_becomeuser" or mtype == "perform_showall":
fin_output += perform_becomeuser(req, callback='')
fin_output += " "
return index(req=req,
title='Manage Accounts',
subtitle=subtitle,
body=[fin_output],
adminarea=0,
authorized=1)
def perform_accesspolicy(req, callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """1. Access policy. [?]""" % CFG_SITE_URL
+ subtitle = """1. Access policy. [?]""" % CFG_SITE_SECURE_URL
account_policy = {}
account_policy[0] = "Users can register new accounts. New accounts automatically activated."
account_policy[1] = "Users can register new accounts. Admin users must activate the accounts."
account_policy[2] = "Only admin can register new accounts. User cannot edit email address."
account_policy[3] = "Only admin can register new accounts. User cannot edit email address or password."
account_policy[4] = "Only admin can register new accounts. User cannot edit email address,password or login method."
site_policy = {}
site_policy[0] = "Normal operation of the site."
site_policy[1] = "Read-only site, all write operations temporarily closed."
site_policy[2] = "Site fully closed."
output = "(Modifications must be done in access_control_config.py) "
output += " Current settings: "
output += "Site status: %s " % (site_policy[CFG_ACCESS_CONTROL_LEVEL_SITE])
output += "Guest accounts allowed: %s " % (CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0 and "Yes" or "No")
output += "Account policy: %s " % (account_policy[CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS])
output += "Allowed email addresses limited: %s " % (CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN and CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN or "Not limited")
output += "Send email to admin when new account: %s " % (CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS == 1 and "Yes" or "No")
output += "Send email to user after creating new account: %s " % (CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1 and "Yes" or "No")
output += "Send email to user when account is activated: %s " % (CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION == 1 and "Yes" or "No")
output += "Send email to user when account is deleted/rejected: %s " % (CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1 and "Yes" or "No")
output += " "
output += "Available 'login via' methods: "
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
methods.sort()
for system in methods:
- output += """%s %s """ % (system, (CFG_EXTERNAL_AUTHENTICATION[system][1] and "(Default)" or ""))
+ output += """%s %s """ % (system, (CFG_EXTERNAL_AUTH_DEFAULT == system and "(Default)" or ""))
output += " Changing the settings: "
output += "Currently, all changes must be done using your favourite editor, and the webserver restarted for changes to take effect. For the settings to change, either look in the guide or in access_control_config.py ."
body = [output]
if callback:
return perform_manageaccounts(req, "perform_accesspolicy", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_accountoverview(req, callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """2. Account overview. [?]""" % CFG_SITE_URL
+ subtitle = """2. Account overview. [?]""" % CFG_SITE_SECURE_URL
output = ""
res = run_sql("SELECT COUNT(*) FROM user WHERE email=''")
output += "Guest accounts: %s " % res[0][0]
res = run_sql("SELECT COUNT(*) FROM user WHERE email!=''")
output += "Registered accounts: %s " % res[0][0]
res = run_sql("SELECT COUNT(*) FROM user WHERE email!='' AND note='0' OR note IS NULL")
output += "Inactive accounts: %s " % res[0][0]
if res[0][0] > 0:
output += ' [Activate/Reject accounts]'
res = run_sql("SELECT COUNT(*) FROM user")
output += " Total nr of accounts: %s " % res[0][0]
body = [output]
if callback:
return perform_manageaccounts(req, "perform_accountoverview", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_createaccount(req, email='', password='', callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """3. Create account. [?]""" % CFG_SITE_URL
+ subtitle = """3. Create account. [?]""" % CFG_SITE_SECURE_URL
output = ""
text = ' Email:\n'
text += ' ' % (email, )
text += ' Password:\n'
text += ' ' % (password, )
output += createhiddenform(action="createaccount",
text=text,
confirm=1,
button="Create")
if confirm in [1, "1"] and email and email_valid_p(email):
res = run_sql("SELECT email FROM user WHERE email=%s", (email,))
if not res:
res = run_sql("INSERT INTO user (email,password, note) values(%s,AES_ENCRYPT(email,%s), '1')", (email, password))
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1:
emailsent = send_new_user_account_warning(email, email, password) == 0
if password:
output += 'Account created with password and activated.'
else:
output += 'Account created without password and activated.'
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1:
if emailsent:
output += ' An email has been sent to the owner of the account.'
else:
output += ' Could not send an email to the owner of the account.'
else:
output += 'An account with the same email already exists.'
elif confirm in [1, "1"]:
output += 'Please specify an valid email-address.'
body = [output]
if callback:
return perform_manageaccounts(req, "perform_createaccount", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyaccountstatus(req, userID, email_user_pattern, limit_to, maxpage, page, callback='yes', confirm=0):
"""set a disabled account to enabled and opposite"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
res = run_sql("SELECT id, email, note FROM user WHERE id=%s", (userID, ))
subtitle = ""
output = ""
if res:
if res[0][2] in [0, "0", None]:
res2 = run_sql("UPDATE user SET note=1 WHERE id=%s", (userID, ))
output += """The account '%s' has been activated.""" % res[0][1]
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION == 1:
emailsent = send_account_activated_message(res[0][1], res[0][1], '*****')
if emailsent:
output += """ An email has been sent to the owner of the account."""
else:
output += """ Could not send an email to the owner of the account."""
elif res[0][2] in [1, "1"]:
res2 = run_sql("UPDATE user SET note=0 WHERE id=%s", (userID, ))
output += """The account '%s' has been set inactive.""" % res[0][1]
else:
output += 'The account id given does not exist.'
body = [output]
if callback:
return perform_modifyaccounts(req, email_user_pattern, limit_to, maxpage, page, content=output, callback='yes')
else:
return addadminbox(subtitle, body)
def perform_editaccount(req, userID, mtype='', content='', callback='yes', confirm=-1):
"""form to modify an account. this method is calling other methods which again is calling this and sending back the output of the method.
if callback, the method will call perform_editcollection, if not, it will just return its output.
userID - id of the user
mtype - the method that called this method.
content - the output from that method."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
res = run_sql("SELECT id, email FROM user WHERE id=%s", (userID, ))
if not res:
if mtype == "perform_deleteaccount":
text = """The selected account has been deleted, to continue editing, go back to 'Manage Accounts'."""
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1:
text += """ An email has been sent to the owner of the account."""
else:
text = """The selected accounts does not exist, please go back and select an account to edit."""
return index(req=req,
title='Edit Account',
subtitle="Edit account",
body=[text],
adminarea=7,
authorized=1)
fin_output = """
- """ % (CFG_SITE_URL, userID, CFG_SITE_URL, userID, CFG_SITE_URL, userID, CFG_SITE_URL, userID)
+ """ % (CFG_SITE_SECURE_URL, userID, CFG_SITE_SECURE_URL, userID, CFG_SITE_SECURE_URL, userID, CFG_SITE_SECURE_URL, userID)
if mtype == "perform_modifylogindata" and content:
fin_output += content
elif mtype == "perform_modifylogindata" or not mtype:
fin_output += perform_modifylogindata(req, userID, callback='')
if mtype == "perform_modifypreferences" and content:
fin_output += content
elif mtype == "perform_modifypreferences" or not mtype:
fin_output += perform_modifypreferences(req, userID, callback='')
if mtype == "perform_deleteaccount" and content:
fin_output += content
elif mtype == "perform_deleteaccount" or not mtype:
fin_output += perform_deleteaccount(req, userID, callback='')
return index(req=req,
title='Edit Account',
subtitle="Edit account '%s'" % res[0][1],
body=[fin_output],
adminarea=7,
authorized=1)
def perform_becomeuser(req, userID='', callback='yes', confirm=0):
"""modify email and password of an account"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """5. Became user. [?]""" % CFG_SITE_URL
+ subtitle = """5. Became user. [?]""" % CFG_SITE_SECURE_URL
res = run_sql("SELECT email FROM user WHERE id=%s", (userID, ))
output = ""
if res:
update_Uid(req, res[0][0])
- redirect_to_url(req, CFG_SITE_URL)
+ redirect_to_url(req, CFG_SITE_SECURE_URL)
else:
output += 'The account id given does not exist.'
body = [output]
if callback:
return perform_editaccount(req, userID, mtype='perform_becomeuser', content=addadminbox(subtitle, body), callback='yes')
else:
return addadminbox(subtitle, body)
def perform_modifylogindata(req, userID, nickname='', email='', password='', callback='yes', confirm=0):
"""modify email and password of an account"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """1. Edit login-data. [?]""" % CFG_SITE_URL
+ subtitle = """1. Edit login-data. [?]""" % CFG_SITE_SECURE_URL
res = run_sql("SELECT id, email, nickname FROM user WHERE id=%s", (userID, ))
output = ""
if res:
if not email and not password:
email = res[0][1]
nickname = res[0][2]
text = ' Account id:%s \n' % userID
text = ' Nickname:\n'
text += ' ' % (nickname, )
text += ' Email:\n'
text += ' ' % (email, )
text += ' Password:\n'
text += ' ' % (password, )
output += createhiddenform(action="modifylogindata",
text=text,
userID=userID,
confirm=1,
button="Modify")
if confirm in [1, "1"] and email and email_valid_p(email):
res = run_sql("SELECT nickname FROM user WHERE nickname=%s AND id<>%s", (nickname, userID))
if res:
output += 'Sorry, the specified nickname is already used.'
else:
res = run_sql("UPDATE user SET email=%s WHERE id=%s", (email, userID))
if password:
res = run_sql("UPDATE user SET password=AES_ENCRYPT(email,%s) WHERE id=%s", (password, userID))
else:
output += 'Password not modified. '
res = run_sql("UPDATE user SET nickname=%s WHERE id=%s", (nickname, userID))
output += 'Nickname/email and/or password modified.'
elif confirm in [1, "1"]:
output += 'Please specify an valid email-address.'
else:
output += 'The account id given does not exist.'
body = [output]
if callback:
return perform_editaccount(req, userID, mtype='perform_modifylogindata', content=addadminbox(subtitle, body), callback='yes')
else:
return addadminbox(subtitle, body)
def perform_modifypreferences(req, userID, login_method='', callback='yes', confirm=0):
"""modify email and password of an account"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """2. Modify preferences. [?]""" % CFG_SITE_URL
+ subtitle = """2. Modify preferences. [?]""" % CFG_SITE_SECURE_URL
res = run_sql("SELECT id, email FROM user WHERE id=%s", (userID, ))
output = ""
if res:
user_pref = get_user_preferences(userID)
if confirm in [1, "1"]:
if login_method:
user_pref['login_method'] = login_method
set_user_preferences(userID, user_pref)
output += "Select default login method: "
text = ""
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
methods.sort()
for system in methods:
text += """%s """ % (system, (user_pref['login_method'] == system and "checked" or ""), system)
output += createhiddenform(action="modifypreferences",
text=text,
confirm=1,
userID=userID,
button="Select")
if confirm in [1, "1"]:
if login_method:
output += """The login method has been changed"""
else:
output += """Nothing to update"""
else:
output += 'The account id given does not exist.'
body = [output]
if callback:
return perform_editaccount(req, userID, mtype='perform_modifypreferences', content=addadminbox(subtitle, body), callback='yes')
else:
return addadminbox(subtitle, body)
def perform_deleteaccount(req, userID, callback='yes', confirm=0):
"""delete account"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """3. Delete account. [?]""" % CFG_SITE_URL
+ subtitle = """3. Delete account. [?]""" % CFG_SITE_SECURE_URL
res = run_sql("SELECT id, email FROM user WHERE id=%s", (userID, ))
output = ""
if res:
if confirm in [0, "0"]:
text = 'Are you sure you want to delete the account with email: "%s"?' % res[0][1]
output += createhiddenform(action="deleteaccount",
text=text,
userID=userID,
confirm=1,
button="Delete")
elif confirm in [1, "1"]:
res2 = run_sql("DELETE FROM user WHERE id=%s", (userID, ))
output += 'Account deleted.'
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1:
emailsent = send_account_deleted_message(res[0][1], res[0][1])
else:
output += 'The account id given does not exist.'
body = [output]
if callback:
return perform_editaccount(req, userID, mtype='perform_deleteaccount', content=addadminbox(subtitle, body), callback='yes')
else:
return addadminbox(subtitle, body)
def perform_rejectaccount(req, userID, email_user_pattern, limit_to, maxpage, page, callback='yes', confirm=0):
"""Delete account and send an email to the owner."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
res = run_sql("SELECT id, email, note FROM user WHERE id=%s", (userID, ))
output = ""
subtitle = ""
if res:
res2 = run_sql("DELETE FROM user WHERE id=%s", (userID, ))
output += 'Account rejected and deleted.'
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1:
if not res[0][2] or res[0][2] == "0":
emailsent = send_account_rejected_message(res[0][1], res[0][1])
elif res[0][2] == "1":
emailsent = send_account_deleted_message(res[0][1], res[0][1])
if emailsent:
output += """ An email has been sent to the owner of the account."""
else:
output += """ Could not send an email to the owner of the account."""
else:
output += 'The account id given does not exist.'
body = [output]
if callback:
return perform_modifyaccounts(req, email_user_pattern, limit_to, maxpage, page, content=output, callback='yes')
else:
return addadminbox(subtitle, body)
def perform_modifyaccounts(req, email_user_pattern='', limit_to=-1, maxpage=MAXPAGEUSERS, page=1, content='', callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """4. Edit accounts. [?]""" % CFG_SITE_URL
+ subtitle = """4. Edit accounts. [?]""" % CFG_SITE_SECURE_URL
output = ""
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
try:
maxpage = int(maxpage)
except:
maxpage = MAXPAGEUSERS
try:
page = int(page)
if page < 1:
page = 1
except:
page = 1
text = ' Email (part of):\n'
text += ' ' % (email_user_pattern, )
text += """Limit to: """ % ((limit_to=="all" and "selected" or ""), (limit_to=="enabled" and "selected" or ""), (limit_to=="disabled" and "selected" or ""))
text += """Accounts per page: """ % ((maxpage==25 and "selected" or ""), (maxpage==50 and "selected" or ""), (maxpage==100 and "selected" or ""), (maxpage==250 and "selected" or ""), (maxpage==500 and "selected" or ""), (maxpage==1000 and "selected" or ""))
output += createhiddenform(action="modifyaccounts",
text=text,
button="search for accounts")
if limit_to not in [-1, "-1"] and maxpage:
options = []
users1 = "SELECT id,email,note FROM user WHERE "
if limit_to == "enabled":
users1 += " email!='' AND note=1"
elif limit_to == "disabled":
users1 += " email!='' AND note=0 OR note IS NULL"
elif limit_to == "guest":
users1 += " email=''"
else:
users1 += " email!=''"
if email_user_pattern:
users1 += " AND email RLIKE %s"
options += [email_user_pattern]
users1 += " ORDER BY email LIMIT %s"
options += [maxpage * page + 1]
try:
users1 = run_sql(users1, tuple(options))
except OperationalError:
users1 = ()
if not users1:
output += 'There are no accounts matching the email given.'
else:
users = []
if maxpage * (page - 1) > len(users1):
page = len(users1) / maxpage + 1
for (id, email, note) in users1[maxpage * (page - 1):(maxpage * page)]:
users.append(['', id, email, (note=="1" and 'Active' or 'Inactive')])
for col in [(((note=="1" and 'Inactivate' or 'Activate'), 'modifyaccountstatus'), ((note == "0" and 'Reject' or 'Delete'), 'rejectaccount'), ),
(('Edit account', 'editaccount'), ),]:
users[-1].append('%s' % (col[0][1], id, email_user_pattern, limit_to, maxpage, page, random.randint(0, 1000), col[0][0]))
for (str, function) in col[1:]:
users[-1][-1] += ' / %s' % (function, id, email_user_pattern, limit_to, maxpage, page, random.randint(0, 1000), str)
users[-1].append('%s' % ('becomeuser', id, email_user_pattern, limit_to, maxpage, page, random.randint(0, 1000), 'Become user'))
last = ""
next = ""
if len(users1) > maxpage:
if page > 1:
last += 'Last Page' % (email_user_pattern, limit_to, maxpage, (page - 1))
if len(users1[maxpage * (page - 1):(maxpage * page)]) == maxpage:
next += 'Next page' % (email_user_pattern, limit_to, maxpage, (page + 1))
output += 'Showing accounts %s-%s:' % (1 + maxpage * (page - 1), maxpage * page)
else:
output += '%s matching account(s):' % len(users1)
output += tupletotable(header=[last, 'id', 'email', 'Status', '', '', next], tuple=users)
else:
output += 'Please select which accounts to find and how many to show per page.'
if content:
output += " %s" % content
body = [output]
if callback:
return perform_manageaccounts(req, "perform_modifyaccounts", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_delegate_startarea(req):
"""start area for lower level delegation of rights."""
# refuse access to guest users:
uid = getUid(req)
if isGuestUser(uid):
return index(req=req,
title='Delegate Rights',
adminarea=0,
authorized=0)
subtitle = 'select what to do'
output = ''
if is_adminuser(req)[0] == 0:
output += """
You are also allowed to be in the Main Admin Area which gives you
the access to the full functionality of WebAccess.
specialized area to set up the delegation rights used in the areas above.
you need to be a web administrator to access the area.
"""
return index(req=req,
title='Delegate Rights',
subtitle=subtitle,
body=[output],
adminarea=0,
authorized=1)
def perform_delegate_adminsetup(req, id_role_admin=0, id_role_delegate=0, confirm=0):
"""lets the webadmins set up the delegation rights for the other roles
id_role_admin - the role to be given delegation rights
id_role_delegate - the role over which the delegation rights are given
confirm - make the connection happen """
subtitle = 'step 1 - select admin role'
admin_roles = acca.acc_get_all_roles()
output = """
This is a specialized area to handle a task that also can be handled
from the "add authorization" interface.
By handling the delegation rights here you get the advantage of
not having to select the correct action (%s) or
remembering the names of available roles.
""" % (DELEGATEADDUSERROLE, )
output += createroleselect(id_role=id_role_admin,
step=1,
button='select admin role',
name='id_role_admin',
action='delegate_adminsetup',
roles=admin_roles)
if str(id_role_admin) != '0':
subtitle = 'step 2 - select delegate role'
name_role_admin = acca.acc_get_role_name(id_role=id_role_admin)
delegate_roles_old = acca.acc_find_delegated_roles(id_role_admin=id_role_admin)
delegate_roles = []
delegate_roles_old_names = []
for role in admin_roles:
if (role,) not in delegate_roles_old:
delegate_roles.append(role)
else:
delegate_roles_old_names.append(role[1])
if delegate_roles_old_names:
delegate_roles_old_names.sort()
names_str = ''
for name in delegate_roles_old_names:
if names_str: names_str += ', '
names_str += name
output += '
Warning: don't hand out delegation rights that can harm the system (e.g. delegating superrole).
"""
output += createhiddenform(action="delegate_adminsetup",
text='let role %s delegate rights over role %s?' % (name_role_admin, name_role_delegate),
id_role_admin=id_role_admin,
id_role_delegate=id_role_delegate,
confirm=1)
if int(confirm):
subtitle = 'step 4 - confirm delegation right added'
# res1 = acca.acc_add_role_action_arguments_names(name_role=name_role_admin,
# name_action=DELEGATEADDUSERROLE,
# arglistid=-1,
# optional=0,
# role=name_role_delegate)
res1 = acca.acc_add_authorization(name_role=name_role_admin,
name_action=DELEGATEADDUSERROLE,
optional=0,
role=name_role_delegate)
if res1:
output += '
confirm: role %s delegates role %s.' % (name_role_admin, name_role_delegate)
else: output += '
sorry, delegation right could not be added, it probably already exists.
'
# see if right hand menu is available
try: body = [output, extra]
except NameError: body = [output]
return index(req=req,
title='Delegate Rights',
subtitle=subtitle,
body=body,
adminarea=1)
def perform_delegate_adduserrole(req, id_role=0, email_user_pattern='', id_user=0, confirm=0):
"""let a lower level web admin add users to a limited set of roles.
id_role - the role to connect to a user
id_user - the user to connect to a role
confirm - make the connection happen """
# finding the allowed roles for this user
id_admin = getUid(req)
id_action = acca.acc_get_action_id(name_action=DELEGATEADDUSERROLE)
actions = acca.acc_find_possible_actions_user(id_user=id_admin, id_action=id_action)
allowed_roles = []
allowed_id_roles = []
for (id, arglistid, name_role_help) in actions[1:]:
id_role_help = acca.acc_get_role_id(name_role=name_role_help)
if id_role_help and [id_role_help, name_role_help, ''] not in allowed_roles:
allowed_roles.append([id_role_help, name_role_help, ''])
allowed_id_roles.append(str(id_role_help))
output = ''
if not allowed_roles:
subtitle = 'no delegation rights'
output += """
You do not have the delegation rights over any roles.
If you think you should have such rights, contact a WebAccess Administrator.
Lower level delegation of access rights to roles.
An administrator with all rights have to give you these rights.
"""
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
output += createroleselect(id_role=id_role, step=1, name='id_role',
action='delegate_adduserrole', roles=allowed_roles)
if str(id_role) != '0' and str(id_role) in allowed_id_roles:
subtitle = 'step 2 - search for users'
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' 2. search for user \n'
text += ' \n' % (email_user_pattern, )
output += createhiddenform(action="delegate_adduserrole",
text=text,
button="search for users",
id_role=id_role)
# pattern is entered
if email_user_pattern:
# users with matching email-address
try:
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
except OperationalError:
users1 = ()
# users that are connected
try:
users2 = run_sql("""SELECT DISTINCT u.id, u.email
FROM user u LEFT JOIN user_accROLE ur ON u.id = ur.id_user
WHERE ur.id_accROLE = %s AND u.email RLIKE %s
ORDER BY u.email """, (id_role, email_user_pattern))
except OperationalError:
users2 = ()
# no users that match the pattern
if not (users1 or users2):
output += '
no qualified users, try new search.
'
# too many matching users
elif len(users1) > MAXSELECTUSERS:
output += '
%s hits, too many qualified users, specify more narrow search. (limit %s)
' % (len(users1), MAXSELECTUSERS)
# show matching users
else:
subtitle = 'step 3 - select a user'
users = []
extrausers = []
for (id, email) in users1:
if (id, email) not in users2: users.append([id,email,''])
for (id, email) in users2:
extrausers.append([-id, email,''])
output += createuserselect(id_user=id_user,
action="delegate_adduserrole",
step=3,
users=users,
extrausers=extrausers,
button="add this user",
id_role=id_role,
email_user_pattern=email_user_pattern)
try: id_user = int(id_user)
except ValueError: pass
# user selected already connected to role
if id_user < 0:
output += '
users in brackets are already attached to the role, try another one...
'
# a user is selected
elif email_out:
subtitle = "step 4 - confirm to add user"
output += createhiddenform(action="delegate_adduserrole",
text='add user %s to role %s?' % (email_out, name_role),
id_role=id_role,
email_user_pattern=email_user_pattern,
id_user=id_user,
confirm=1)
# it is confirmed that this user should be added
if confirm:
# add user
result = acca.acc_add_user_role(id_user=id_user, id_role=id_role)
if result and result[2]:
subtitle = 'step 5 - confirm user added'
output += '
confirm: user %s added to role %s.
' % (email_out, name_role)
else:
subtitle = 'step 5 - user could not be added'
output += '
remove users from the roles you have delegating rights to.
""" % (id_role, )
return index(req=req,
title='Connect users to roles',
subtitle=subtitle,
body=[output, extra],
adminarea=1,
authorized=1)
def perform_delegate_deleteuserrole(req, id_role=0, id_user=0, confirm=0):
"""let a lower level web admin remove users from a limited set of roles.
id_role - the role to connect to a user
id_user - the user to connect to a role
confirm - make the connection happen """
subtitle = 'in progress...'
output = '
in progress...
'
# finding the allowed roles for this user
id_admin = getUid(req)
id_action = acca.acc_get_action_id(name_action=DELEGATEADDUSERROLE)
actions = acca.acc_find_possible_actions_user(id_user=id_admin, id_action=id_action)
output = ''
if not actions:
subtitle = 'no delegation rights'
output += """
You do not have the delegation rights over any roles.
If you think you should have such rights, contact a WebAccess Administrator.
Lower level delegation of access rights to roles.
An administrator with all rights have to give you these rights.
"""
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
# create list of allowed roles
allowed_roles = []
allowed_id_roles = []
for (id, arglistid, name_role_help) in actions[1:]:
id_role_help = acca.acc_get_role_id(name_role=name_role_help)
if id_role_help and [id_role_help, name_role_help, ''] not in allowed_roles:
allowed_roles.append([id_role_help, name_role_help, ''])
allowed_id_roles.append(str(id_role_help))
output += createroleselect(id_role=id_role, step=1,
action='delegate_deleteuserrole', roles=allowed_roles)
if str(id_role) != '0' and str(id_role) in allowed_id_roles:
subtitle = 'step 2 - select user'
users = acca.acc_get_role_users(id_role)
output += createuserselect(id_user=id_user,
step=2,
action='delegate_deleteuserrole',
users=users,
id_role=id_role)
if str(id_user) != '0':
subtitle = 'step 3 - confirm delete of user'
email_user = acca.acc_get_user_email(id_user=id_user)
output += createhiddenform(action="delegate_deleteuserrole",
text='delete user %s from %s?'
% (headerstrong(user=id_user), headerstrong(role=id_role)),
id_role=id_role,
id_user=id_user,
confirm=1)
if confirm:
res = acca.acc_delete_user_role(id_user=id_user, id_role=id_role)
if res:
subtitle = 'step 4 - confirm user deleted from role'
output += '
confirm: deleted user %s from role %s.
' % (email_user, name_role)
else:
subtitle = 'step 4 - user could not be deleted'
output += 'sorry, but user could not be deleted user is probably already deleted.'
extra = """
'
return output
def perform_addrole(req, id_role=0, name_role='', description='put description here.', firerole_def_src=CFG_ACC_EMPTY_ROLE_DEFINITION_SRC, confirm=0):
"""form to add a new role with these values:
name_role - name of the new role
description - optional description of the role """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
name_role = cleanstring(name_role)
title='Add Role'
subtitle = 'step 1 - give values to the requested fields'
output = """
""" % (escape(name_role, '"'), escape(description), escape(firerole_def_src))
if name_role:
# description must be changed before submitting
subtitle = 'step 2 - confirm to add role'
internaldesc = ''
if description != 'put description here.':
internaldesc = description
try:
firerole_def_ser = serialize(compile_role_definition(firerole_def_src))
except InvenioWebAccessFireroleError, msg:
output += "%s" % msg
else:
text = """
add role with: \n
name: %s """ % (name_role, )
if internaldesc:
text += 'description: %s?\n' % (description, )
output += createhiddenform(action="addrole",
text=text,
name_role=escape(name_role, '"'),
description=escape(description, '"'),
firerole_def_src=escape(firerole_def_src, '"'),
confirm=1)
if confirm not in ["0", 0]:
result = acca.acc_add_role(name_role=name_role,
description=internaldesc,
firerole_def_ser=firerole_def_ser,
firerole_def_src=firerole_def_src)
if result:
subtitle = 'step 3 - role added'
output += '
role added:
'
result = list(result)
result[3] = result[3].replace('\n', ' ')
result = tuple(result)
output += tupletotable(header=['id', 'role name', 'description', 'firewall like role definition'],
tuple=[result])
else:
subtitle = 'step 3 - role could not be added'
output += '
sorry, could not add role, role with the same name probably exists.
'
id_role = acca.acc_get_role_id(name_role=name_role)
extra = """
""" % (id_role, name_role, id_role, name_role)
try: body = [output, extra]
except NameError: body = [output]
return index(req=req,
title=title,
body=body,
subtitle=subtitle,
adminarea=3)
def perform_modifyrole(req, id_role='0', name_role='', description='put description here.', firerole_def_src='', modified='0', confirm=0):
"""form to add a new role with these values:
name_role - name of the role to be changed
description - optional description of the role
firerole_def_src - optional firerole like definition of the role
"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
ret = acca.acc_get_role_details(id_role)
if ret and modified =='0':
name_role = ret[1]
description = ret[2]
firerole_def_src = ret[3]
if not firerole_def_src or firerole_def_src == '' or firerole_def_src is None:
firerole_def_src = 'deny any'
name_role = cleanstring(name_role)
title='Modify Role'
subtitle = 'step 1 - give values to the requested fields and confirm to modify role'
output = """
""" % (id_role, escape(name_role), escape(description), escape(firerole_def_src))
if modified in [1, '1']:
# description must be changed before submitting
internaldesc = ''
if description != 'put description here.':
internaldesc = description
text = """
modify role with: \n
name: %s """ % (name_role, )
if internaldesc:
text += 'description: %s? ' % (description, )
text += 'firewall like role definition: %s' % firerole_def_src.replace('\n', ' ')
try:
firerole_def_ser = serialize(compile_role_definition(firerole_def_src))
except InvenioWebAccessFireroleError, msg:
subtitle = 'step 2 - role could not be modified'
output += '
sorry, could not modify role because of troubles with its definition: %s
' % msg
else:
output += createhiddenform(action="modifyrole",
text=text,
id_role = id_role,
name_role=escape(name_role, True),
description=escape(description, True),
firerole_def_src=escape(firerole_def_src, True),
modified=1,
confirm=1)
if confirm not in ["0", 0]:
result = acca.acc_update_role(id_role, name_role=name_role,
description=internaldesc, firerole_def_ser=firerole_def_ser, firerole_def_src=firerole_def_src)
if result:
subtitle = 'step 2 - role modified'
output += '
role modified:
'
output += tupletotable(header=['id', 'role name',
'description', 'firewall like role definition'],
tuple=[(id_role, name_role, description, firerole_def_src.replace('\n', ' '))])
else:
subtitle = 'step 2 - role could not be modified'
output += '
sorry, could not modify role, please contact the administrator.
'
body = [output]
return index(req=req,
title=title,
body=body,
subtitle=subtitle,
adminarea=3)
def perform_deleterole(req, id_role="0", confirm=0):
"""select a role and show all connected information,
users - users that can access the role.
actions - actions with possible authorizations."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
title = 'Delete role'
subtitle = 'step 1 - select role to delete'
name_role = acca.acc_get_role_name(id_role=id_role)
output = createroleselect(id_role=id_role,
action="deleterole",
step=1,
roles=acca.acc_get_all_roles(),
button="delete role")
if id_role != "0" and name_role:
subtitle = 'step 2 - confirm delete of role'
output += roledetails(id_role=id_role)
output += createhiddenform(action="deleterole",
text='delete role %s and all connections?' % (name_role, ),
id_role=id_role,
confirm=1)
if confirm:
res = acca.acc_delete_role(id_role=id_role)
subtitle = 'step 3 - confirm role deleted'
if res:
output += "
confirm: role %s deleted. " % (name_role, )
output += "%s entries were removed.
" % (res, )
else:
output += "
sorry, the role could not be deleted.
"
elif id_role != "0":
output += '
the role has been deleted...
'
return index(req=req,
title=title,
subtitle=subtitle,
body=[output],
adminarea=3)
def perform_showroledetails(req, id_role):
"""show the details of a role."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
output = createroleselect(id_role=id_role,
action="showroledetails",
step=1,
roles=acca.acc_get_all_roles(),
button="select role")
if id_role not in [0, '0']:
name_role = acca.acc_get_role_name(id_role=id_role)
output += roledetails(id_role=id_role)
extra = """
'
return details
def perform_adduserrole(req, id_role='0', email_user_pattern='', id_user='0', confirm=0):
"""create connection between user and role.
id_role - id of the role to add user to
email_user_pattern - search for users using this pattern
id_user - id of user to add to the role. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
title = 'Connect user to role '
subtitle = 'step 1 - select a role'
output = createroleselect(id_role=id_role,
action="adduserrole",
step=1,
roles=acca.acc_get_all_roles())
# role is selected
if id_role != "0":
title += name_role
subtitle = 'step 2 - search for users'
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' 2. search for user \n'
text += ' \n' % (email_user_pattern, )
output += createhiddenform(action="adduserrole",
text=text,
button="search for users",
id_role=id_role)
# pattern is entered
if email_user_pattern:
# users with matching email-address
try:
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
except OperationalError:
users1 = ()
# users that are connected
try:
users2 = run_sql("""SELECT DISTINCT u.id, u.email
FROM user u LEFT JOIN user_accROLE ur ON u.id = ur.id_user
WHERE ur.id_accROLE = %s AND u.email RLIKE %s
ORDER BY u.email """, (id_role, email_user_pattern))
except OperationalError:
users2 = ()
# no users that match the pattern
if not (users1 or users2):
output += '
no qualified users, try new search.
'
elif len(users1) > MAXSELECTUSERS:
output += '
%s hits, too many qualified users, specify more narrow search. (limit %s)
' % (len(users1), MAXSELECTUSERS)
# show matching users
else:
subtitle = 'step 3 - select a user'
users = []
extrausers = []
for (user_id, email) in users1:
if (user_id, email) not in users2: users.append([user_id,email,''])
for (user_id, email) in users2:
extrausers.append([-user_id, email,''])
output += createuserselect(id_user=id_user,
action="adduserrole",
step=3,
users=users,
extrausers=extrausers,
button="add this user",
id_role=id_role,
email_user_pattern=email_user_pattern)
try: id_user = int(id_user)
except ValueError: pass
# user selected already connected to role
if id_user < 0:
output += '
users in brackets are already attached to the role, try another one...
'
# a user is selected
elif email_out:
subtitle = "step 4 - confirm to add user"
output += createhiddenform(action="adduserrole",
text='add user %s to role %s?' % (email_out, name_role),
id_role=id_role,
email_user_pattern=email_user_pattern,
id_user=id_user,
confirm=1)
# it is confirmed that this user should be added
if confirm:
# add user
result = acca.acc_add_user_role(id_user=id_user, id_role=id_role)
if result and result[2]:
subtitle = 'step 5 - confirm user added'
output += '
confirm: user %s added to role %s.
' % (email_out, name_role)
else:
subtitle = 'step 5 - user could not be added'
output += '
""" % (id_role, name_role, id_role, name_role, id_role, name_role)
return index(req=req,
title=title,
subtitle=subtitle,
body=[output, extra],
adminarea=3)
def perform_addroleuser(req, email_user_pattern='', id_user='0', id_role='0', confirm=0):
"""delete connection between role and user.
id_role - id of role to disconnect
id_user - id of user to disconnect. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
# used to sort roles, and also to determine right side links
con_roles = []
not_roles = []
title = 'Connect user to roles'
subtitle = 'step 1 - search for users'
# clean email search string
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' 1. search for user \n'
text += ' \n' % (email_user_pattern, )
output = createhiddenform(action='addroleuser',
text=text,
button='search for users',
id_role=id_role)
if email_user_pattern:
subtitle = 'step 2 - select user'
try:
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
except OperationalError:
users1 = ()
users = []
for (id, email) in users1: users.append([id, email, ''])
# no users
if not users:
output += '
no qualified users, try new search.
'
# too many users
elif len(users) > MAXSELECTUSERS:
output += '
%s hits, too many qualified users, specify more narrow search. (limit %s)
' % (len(users), MAXSELECTUSERS)
# ok number of users
else:
output += createuserselect(id_user=id_user,
action='addroleuser',
step=2,
users=users,
button='select user',
email_user_pattern=email_user_pattern)
if int(id_user):
subtitle = 'step 3 - select role'
# roles the user is connected to
role_ids = acca.acc_get_user_roles(id_user=id_user)
# all the roles, lists are sorted on the background of these...
all_roles = acca.acc_get_all_roles()
# sort the roles in connected and not connected roles
for (id, name, description, dummy, dummy) in all_roles:
if id in role_ids: con_roles.append([-id, name, description])
else: not_roles.append([id, name, description])
# create roleselect
output += createroleselect(id_role=id_role,
action='addroleuser',
step=3,
roles=not_roles,
extraroles=con_roles,
extrastamp='(connected)',
button='add this role',
email_user_pattern=email_user_pattern,
id_user=id_user)
if int(id_role) < 0:
name_role = acca.acc_get_role_name(id_role=-int(id_role))
output += '
role %s already connected to the user, try another one...
' % (name_role, )
elif int(id_role):
subtitle = 'step 4 - confirm to add role to user'
output += createhiddenform(action='addroleuser',
text='add role %s to user %s?' % (name_role, email_out),
email_user_pattern=email_user_pattern,
id_user=id_user,
id_role=id_role,
confirm=1)
if confirm:
# add role
result = acca.acc_add_user_role(id_user=id_user, id_role=id_role)
if result and result[2]:
subtitle = 'step 5 - confirm role added'
output += '
confirm: role %s added to user %s.
' % (name_role, email_out)
else:
subtitle = 'step 5 - role could not be added'
output += '
""" % (id_role, name_role)
return index(req=req,
title=title,
subtitle=subtitle,
body=[output, extra],
adminarea=5)
def perform_deleteuserrole(req, id_role='0', id_user='0', reverse=0, confirm=0):
"""delete connection between role and user.
id_role - id of role to disconnect
id_user - id of user to disconnect. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
title = 'Remove user from role'
email_user = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
output = ''
if reverse in [0, '0']:
adminarea = 3
subtitle = 'step 1 - select the role'
output += createroleselect(id_role=id_role,
action="deleteuserrole",
step=1,
roles=acca.acc_get_all_roles())
if id_role != "0":
subtitle = 'step 2 - select the user'
output += createuserselect(id_user=id_user,
action="deleteuserrole",
step=2,
users=acca.acc_get_role_users(id_role=id_role),
id_role=id_role)
else:
adminarea = 5
# show only if user is connected to a role, get users connected to roles
users = run_sql("""SELECT DISTINCT(u.id), u.email, u.note
FROM user u LEFT JOIN user_accROLE ur
ON u.id = ur.id_user
WHERE ur.id_accROLE != 'NULL' AND u.email != ''
ORDER BY u.email """)
has_roles = 1
# check if the user is connected to any roles
for (id, email, note) in users:
if str(id) == str(id_user): break
# user not connected to a role
else:
subtitle = 'step 1 - user not connected'
output += '
no need to remove roles from user %s, user is not connected to any roles.
' % (email_user, )
has_roles, id_user = 0, '0' # stop the rest of the output below...
# user connected to roles
if has_roles:
output += createuserselect(id_user=id_user,
action="deleteuserrole",
step=1,
users=users,
reverse=reverse)
if id_user != "0":
subtitle = 'step 2 - select the role'
role_ids = acca.acc_get_user_roles(id_user=id_user)
all_roles = acca.acc_get_all_roles()
roles = []
for (id, name, desc, dummy, dummy) in all_roles:
if id in role_ids: roles.append([id, name, desc])
output += createroleselect(id_role=id_role,
action="deleteuserrole",
step=2,
roles=roles,
id_user=id_user,
reverse=reverse)
if id_role != '0' and id_user != '0':
subtitle = 'step 3 - confirm delete of user'
output += createhiddenform(action="deleteuserrole",
text='delete user %s from %s?' % (headerstrong(user=id_user), headerstrong(role=id_role)),
id_role=id_role,
id_user=id_user,
reverse=reverse,
confirm=1)
if confirm:
res = acca.acc_delete_user_role(id_user=id_user, id_role=id_role)
if res:
subtitle = 'step 4 - confirm delete of user'
output += '
confirm: deleted user %s from role %s.
' % (email_user, name_role)
else:
subtitle = 'step 4 - user could not be deleted'
output += 'sorry, but user could not be deleted user is probably already deleted.'
extra = ''
if str(id_role) != "0":
extra += """
""" % (id_user, email_user, email_user)
extra += '
'
if extra: body = [output, extra]
else: body = [output]
return index(req=req,
title=title,
subtitle=subtitle,
body=body,
adminarea=adminarea)
def perform_showuserdetails(req, id_user=0):
"""show the details of a user. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
if id_user not in [0, '0']:
output = userdetails(id_user=id_user)
email_user = acca.acc_get_user_email(id_user=id_user)
extra = """
' % (email_user, )
return details
def perform_addauthorization(req, id_role="0", id_action="0", optional=0, reverse="0", confirm=0, **keywords):
""" form to add new connection between user and role:
id_role - role to connect
id_action - action to connect
reverse - role or action first? """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
# values that might get used
name_role = acca.acc_get_role_name(id_role=id_role) or id_role
name_action = acca.acc_get_action_name(id_action=id_action) or id_action
optional = optional == 'on' and 1 or int(optional)
extra = """
"""
# create the page according to which step the user is on
# role -> action -> arguments
if reverse in ["0", 0]:
adminarea = 3
subtitle = 'step 1 - select role'
output = createroleselect(id_role=id_role,
action="addauthorization",
step=1,
roles=acca.acc_get_all_roles(),
reverse=reverse)
if str(id_role) != "0":
subtitle = 'step 2 - select action'
rolacts = acca.acc_get_role_actions(id_role)
allhelp = acca.acc_get_all_actions()
allacts = []
for r in allhelp:
if r not in rolacts: allacts.append(r)
output += createactionselect(id_action=id_action,
action="addauthorization",
step=2,
actions=rolacts,
extraactions=allacts,
id_role=id_role,
reverse=reverse)
# action -> role -> arguments
else:
adminarea = 4
subtitle = 'step 1 - select action'
output = createactionselect(id_action=id_action,
action="addauthorization",
step=1,
actions=acca.acc_get_all_actions(),
reverse=reverse)
if str(id_action) != "0":
subtitle = 'step 2 - select role'
actroles = acca.acc_get_action_roles(id_action)
allhelp = acca.acc_get_all_roles()
allroles = []
for r in allhelp:
if r not in actroles: allroles.append(r)
output += createroleselect(id_role=id_role,
action="addauthorization",
step=2,
roles=actroles,
extraroles=allroles,
id_action=id_action,
reverse=reverse)
# ready for step 3 no matter which direction we took to get here
if id_action != "0" and id_role != "0":
# links to adding authorizations in the other direction
if str(reverse) == "0":
extra += """
connect %s to %s for any arguments
connect %s to %s for only these argument cases:
""" % (optional and 'checked="checked"' or '', name_role, name_action, not optional and 'checked="checked"' or '', name_role, name_action)
# list the arguments
allkeys = 1
for key in res_keys:
output += '%s \n \n'
output = output[:-8] + ' \n'
output += '\n'
# ask for confirmation
if str(allkeys) != "0" or optional:
keys = keywords.keys()
keys.reverse()
subtitle = 'step 4 - confirm add of authorization\n'
text = """
create connection between
%s
""" % (headerstrong(role=name_role, action=name_action, query=0), )
if optional:
text += 'withouth arguments'
keywords = {}
else:
for key in keys:
text += '%s: %s \n' % (escape(key), escape(keywords[key]))
output += createhiddenform(action="addauthorization",
text=text,
id_role=id_role,
id_action=id_action,
reverse=reverse,
confirm=1,
optional=optional,
**keywords)
# show existing authorizations, found authorizations further up in the code...
# res_auths = acca.acc_find_possible_actions(id_role, id_action)
output += '
existing authorizations:
'
if res_auths:
output += tupletotable(header=res_auths[0], tuple=res_auths[1:])
# shortcut to modifying authorizations
extra += """
sorry, authorization could not be added, it probably already exists
'
# trying to put extra link on the right side
try: body = [output, extra]
except NameError: body = [output]
return index(req=req,
title = 'Create entry for new authorization',
subtitle=subtitle,
body=body,
adminarea=adminarea)
def perform_deleteroleaction(req, id_role="0", id_action="0", reverse=0, confirm=0):
"""delete all connections between a role and an action.
id_role - id of the role
id_action - id of the action
reverse - 0: ask for role first
1: ask for action first"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
title = 'Remove action from role '
if reverse in ["0", 0]:
# select role -> action
adminarea = 3
subtitle = 'step 1 - select a role'
output = createroleselect(id_role=id_role,
action="deleteroleaction",
step=1,
roles=acca.acc_get_all_roles(),
reverse=reverse)
if id_role != "0":
rolacts = acca.acc_get_role_actions(id_role=id_role)
subtitle = 'step 2 - select the action'
output += createactionselect(id_action=id_action,
action="deleteroleaction",
step=2,
actions=rolacts,
reverse=reverse,
id_role=id_role,
button="remove connection and all authorizations")
else:
# select action -> role
adminarea = 4
subtitle = 'step 1 - select an action'
output = createactionselect(id_action=id_action,
action="deleteroleaction",
step=1,
actions=acca.acc_get_all_actions(),
reverse=reverse)
if id_action != "0":
actroles = acca.acc_get_action_roles(id_action=id_action)
subtitle = 'step 2 - select the role'
output += createroleselect(id_role=id_role,
action="deleteroleaction",
step=2,
roles=actroles,
button="remove connection and all authorizations",
id_action=id_action,
reverse=reverse)
if id_action != "0" and id_role != "0":
subtitle = 'step 3 - confirm to remove authorizations'
# ask for confirmation
res = acca.acc_find_possible_actions(id_role, id_action)
if res:
output += '
authorizations that will be deleted:
'
output += tupletotable(header=res[0], tuple=res[1:])
output += createhiddenform(action="deleteroleaction",
text='remove %s from %s' % (headerstrong(action=id_action), headerstrong(role=id_role)),
confirm=1,
id_role=id_role,
id_action=id_action,
reverse=reverse)
else:
output += 'no authorizations'
# confirmation is given
if confirm:
subtitle = 'step 4 - confirm authorizations removed '
res = acca.acc_delete_role_action(id_role=id_role, id_action=id_action)
if res:
output += '
confirm: removed %s from %s ' % (headerstrong(action=id_action), headerstrong(role=id_role))
output += '%s entries were removed.
' % (res, )
else:
output += '
sorry, no entries could be removed.
'
return index(req=req,
title=title,
subtitle=subtitle,
body=[output],
adminarea=adminarea)
def perform_modifyauthorizations(req, id_role="0", id_action="0", reverse=0, confirm=0, errortext='', sel='', authids=[]):
"""given ids of a role and an action, show all possible action combinations
with checkboxes and allow user to access other functions.
id_role - id of the role
id_action - id of the action
reverse - 0: ask for role first
1: ask for action first
sel - which button and modification that is selected
errortext - text to print when no connection exist between role and action
authids - ids of checked checkboxes """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
name_role = acca.acc_get_role_name(id_role)
name_action = acca.acc_get_action_name(id_action)
output = ''
try: id_role, id_action, reverse = int(id_role), int(id_action), int(reverse)
except ValueError: pass
extra = """
\n'
if not reverse:
# role -> action
adminarea = 3
subtitle = 'step 1 - select the role'
output += createroleselect(id_role=str(id_role),
action="modifyauthorizations",
step=1,
roles=acca.acc_get_all_roles(),
reverse=reverse)
if id_role:
rolacts = acca.acc_get_role_actions(id_role=id_role)
subtitle = 'step 2 - select the action'
output += createactionselect(id_action=str(id_action),
action="modifyauthorizations",
step=2,
actions=rolacts,
id_role=id_role,
reverse=reverse)
else:
adminarea = 4
# action -> role
subtitle = 'step 1 - select the action'
output += createactionselect(id_action=str(id_action),
action="modifyauthorizations",
step=1,
actions=acca.acc_get_all_actions(),
reverse=reverse)
if id_action:
actroles = acca.acc_get_action_roles(id_action=id_action)
subtitle = 'step 2 - select the role'
output += createroleselect(id_role=str(id_role),
action="modifyauthorizations",
step=2,
roles=actroles,
id_action=id_action,
reverse=reverse)
if errortext: output += '
%s
' % (errortext, )
if id_role and id_action:
# adding to main area
if type(authids) is not list: authids = [authids]
subtitle = 'step 3 - select groups and modification'
# get info
res = acca.acc_find_possible_actions(id_role, id_action)
# clean the authids
hiddenids = []
if sel in ['delete selected']:
hiddenids = authids[:]
elif sel in ['split groups', 'merge groups']:
for authid in authids:
arghlp = res[int(authid)][0]
if authid not in hiddenids and arghlp not in [-1, '-1', 0, '0']: hiddenids.append(authid)
authids = hiddenids[:]
if confirm:
# do selected modification and output with new authorizations
if sel == 'split groups':
res = splitgroups(id_role, id_action, authids)
elif sel == 'merge groups':
res = mergegroups(id_role, id_action, authids)
elif sel == 'delete selected':
res = deleteselected(id_role, id_action, authids)
authids = []
res = acca.acc_find_possible_actions(id_role, id_action)
output += 'authorizations after %s. \n' % (sel, )
elif sel and authids:
output += 'confirm choice of authorizations and modification. \n'
else:
output += 'select authorizations and perform modification. \n'
if not res:
errortext = 'all connections deleted, try different '
if reverse in ["0", 0]:
return perform_modifyauthorizations(req=req, id_role=id_role, errortext=errortext + 'action.')
else:
return perform_modifyauthorizations(req=req, id_action=id_action, reverse=reverse, errortext=errortext + 'role.')
# display
output += modifyauthorizationsmenu(id_role, id_action, header=res[0], tuple=res[1:], checked=authids, reverse=reverse)
if sel and authids:
subtitle = 'step 4 - confirm to perform modification'
# form with hidden authids
output += ''
# tried to perform modification without something selected
elif sel and not authids and not confirm:
output += '
no valid groups selected
'
# trying to put extra link on the right side
try:
body = [output, extra]
except NameError:
body = [output]
# Display the page
return index(req=req,
title='Modify Authorizations',
subtitle=subtitle,
body=body,
adminarea=adminarea)
def modifyauthorizationsmenu(id_role, id_action, tuple=[], header=[],
checked=[], reverse=0):
"""create table with header and checkboxes, used for multiple choice.
makes use of tupletotable to add the actual table
id_role - selected role, hidden value in the form
id_action - selected action, hidden value in the form
tuple - all rows to be put in the table (with checkboxes)
header - column headers, empty strings added at start and end
checked - ids of rows to be checked """
if not tuple:
return 'no authorisations...'
argnum = len(acca.acc_get_action_keywords(id_action=id_action))
tuple2 = []
for t in tuple:
tuple2.append(t[:])
tuple2 = addcheckboxes(datalist=tuple2, name='authids', startindex=1,
checked=checked)
hidden = ' \n' \
% (id_role, )
hidden += ' \n' \
% (id_action, )
hidden += ' \n' \
% (reverse, )
button = '\n'
if argnum > 1:
button += '\n'
button += '\n'
hdrstr = ''
for h in [''] + header + ['']:
hdrstr += '
%s
\n' % (h, )
if hdrstr:
hdrstr = '
\n%s\n
\n' % (hdrstr, )
output = '\n'
return output
def splitgroups(id_role=0, id_action=0, authids=[]):
"""get all the old ones, gather up the arglistids find a list of
arglistidgroups to be split, unique get all actions in groups outside
of the old ones, (old arglistid is allowed).
show them like in showselect. """
if not id_role or not id_action or not authids:
return 0
# find all the actions
datalist = acca.acc_find_possible_actions(id_role, id_action)
if type(authids) is str:
authids = [authids]
for i in range(len(authids)):
authids[i] = int(authids[i])
# argumentlistids of groups to be split
splitgrps = []
for authid in authids:
hlp = datalist[authid][0]
if hlp not in splitgrps and authid in range(1, len(datalist)):
splitgrps.append(hlp)
# split groups and return success or failure
result = 1
for splitgroup in splitgrps:
result = 1 and acca.acc_split_argument_group(id_role, id_action,
splitgroup)
return result
def mergegroups(id_role=0, id_action=0, authids=[]):
"""get all the old ones, gather up the argauthids find a list
of arglistidgroups to be split, unique get all actions in groups
outside of the old ones, (old arglistid is allowed).
show them like in showselect."""
if not id_role or not id_action or not authids:
return 0
datalist = acca.acc_find_possible_actions(id_role, id_action)
if type(authids) is str:
authids = [authids]
for i in range(len(authids)):
authids[i] = int(authids[i])
# argumentlistids of groups to be merged
mergegroups = []
for authid in authids:
hlp = datalist[authid][0]
if hlp not in mergegroups and authid in range(1, len(datalist)):
mergegroups.append(hlp)
# merge groups and return success or failure
if acca.acc_merge_argument_groups(id_role, id_action, mergegroups):
return 1
else:
return 0
def deleteselected(id_role=0, id_action=0, authids=[]):
"""delete checked authorizations/possible actions, ids in authids.
id_role - role to delete from
id_action - action to delete from
authids - listids for which possible actions to delete."""
if not id_role or not id_action or not authids:
return 0
if type(authids) in [str, int]:
authids = [authids]
for i in range(len(authids)):
authids[i] = int(authids[i])
result = acca.acc_delete_possible_actions(id_role=id_role,
id_action=id_action,
authids=authids)
return result
def headeritalic(**ids):
"""transform keyword=value pairs to string with value in italics.
**ids - a dictionary of pairs to create string from """
output = ''
value = ''
table = ''
for key in ids.keys():
if key in ['User', 'user']:
value, table = 'email', 'user'
elif key in ['Role', 'role']:
value, table = 'name', 'accROLE'
elif key in ['Action', 'action']:
value, table = 'name', 'accACTION'
else:
if output:
output += ' and '
output += ' %s %s' % (key, ids[key])
continue
res = run_sql("""SELECT %%s FROM %s WHERE id = %%s""" % table, (value, ids[key]))
if res:
if output:
output += ' and '
output += ' %s %s' % (key, res[0][0])
return output
def headerstrong(query=1, **ids):
"""transform keyword=value pairs to string with value in strong text.
**ids - a dictionary of pairs to create string from
query - 1 -> try to find names to ids of role, user and action.
0 -> do not try to find names, use the value passed on """
output = ''
value = ''
table = ''
for key in ids.keys():
if key in ['User', 'user']:
value, table = 'email', 'user'
elif key in ['Role', 'role']:
value, table = 'name', 'accROLE'
elif key in ['Action', 'action']:
value, table = 'name', 'accACTION'
else:
if output:
output += ' and '
output += ' %s %s' % (key, ids[key])
continue
if query:
res = run_sql("""SELECT %%s FROM %s WHERE id = %%s""" % table, (value, ids[key]))
if res:
if output:
output += ' and '
output += ' %s %s' % (key, res[0][0])
else:
if output:
output += ' and '
output += ' %s %s' % (key, ids[key])
return output
def startpage():
"""create the menu for the startpage"""
body = """
"""
return body
def rankarea():
return "Rankmethod area"
def perform_simpleauthorization(req, id_role=0, id_action=0):
"""show a page with simple overview of authorizations between a
connected role and action. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0:
return mustloginpage(req, auth_message)
res = acca.acc_find_possible_actions(id_role, id_action)
if res:
extra = createhiddenform(action='modifyauthorizations',
button='modify authorizations',
id_role=id_role,
id_action=id_action)
output = '
authorizations for %s:
' \
% (headerstrong(action=id_action, role=id_role), )
output += tupletotable(header=res[0], tuple=res[1:], extracolumn=extra)
else:
output = 'no details to show'
return index(req=req,
title='Simple authorization details',
subtitle='simple authorization details',
body=[output],
adminarea=3)
def perform_showroleusers(req, id_role=0):
"""show a page with simple overview of a role and connected users. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0:
return mustloginpage(req, auth_message)
res = acca.acc_get_role_users(id_role=id_role)
name_role = acca.acc_get_role_name(id_role=id_role)
if res:
users = []
for (role_id, name, dummy) in res:
users.append([role_id, name, 'show user details' % (role_id, )])
output = '
users connected to %s:
' \
% (headerstrong(role=id_role), )
output += tupletotable(header=['id', 'name', ''], tuple=users)
else:
output = 'no users connected to role %s' \
% (name_role, )
extra = """
""" % (id_role, )
return index(req=req,
title='Users connected to role %s' % (name_role, ),
subtitle='simple details',
body=[output, extra],
adminarea=3)
def createselect(id_input="0", label="", step=0, name="",
action="", list=[], extralist=[], extrastamp='',
button="", **hidden):
"""create form with select and hidden values
id - the one to choose as selected if exists
label - label shown to the left of the select
name - the name of the select on which to reference it
list - primary list to select from
extralist - list of options to be put in paranthesis
extrastamp - stamp extralist entries with this if not ''
usually paranthesis around the entry
button - the value/text to be put on the button
**hidden - name=value pairs to be put as hidden in the form. """
step = step and '%s. ' % step or ''
output = '\n'
return output
def createactionselect(id_action="0", label="select action", step=0,
name="id_action", action="", actions=[], extraactions=[],
extrastamp='', button="select action", **hidden):
"""create a select for roles in a form. see createselect."""
return createselect(id_input=id_action, label=label, step=step, name=name,
action=action, list=actions, extralist=extraactions,
extrastamp=extrastamp, button=button, **hidden)
def createroleselect(id_role="0", label="select role", step=0, name="id_role",
action="", roles=[], extraroles=[], extrastamp='',
button="select role", **hidden):
"""create a select for roles in a form. see createselect."""
return createselect(id_input=id_role, label=label, step=step, name=name,
action=action, list=roles, extralist=extraroles, extrastamp=extrastamp,
button=button, **hidden)
def createuserselect(id_user="0", label="select user", step=0, name="id_user",
action="", users=[], extrausers=[], extrastamp='(connected)',
button="select user", **hidden):
"""create a select for users in a form.see createselect."""
return createselect(id_input=id_user, label=label, step=step, name=name,
action=action, list=users, extralist=extrausers, extrastamp=extrastamp,
button=button, **hidden)
def cleanstring(txt='', comma=0):
"""clean all the strings before submitting to access control admin.
remove characters not letter, number or underscore, also remove leading
underscores and numbers. return cleaned string.
str - string to be cleaned
comma - 1 -> allow the comma to divide multiple arguments
0 -> wash commas as well """
# remove not allowed characters
txt = re.sub(r'[^a-zA-Z0-9_,]', '', txt)
# split string on commas
items = txt.split(',')
txt = ''
for item in items:
if not item:
continue
if comma and txt:
txt += ','
# create valid variable names
txt += re.sub(r'^([0-9_])*', '', item)
return txt
def cleanstring_argumentvalue(txt=''):
"""clean the value of an argument before submitting it.
allowed characters: a-z A-Z 0-9 _ * and space
txt - string to be cleaned """
# remove not allowed characters
txt = re.sub(r'[^a-zA-Z0-9_ *.]', '', txt)
# trim leading and ending spaces
txt = re.sub(r'^ *| *$', '', txt)
return txt
def cleanstring_email(txt=''):
"""clean the string and return a valid email address.
txt - string to be cleaned """
# remove not allowed characters
txt = re.sub(r'[^a-zA-Z0-9_.@-]', '', txt)
return txt
def check_email(txt=''):
"""control that submitted emails are correct.
this little check is not very good, but better than nothing. """
r = re.compile(r'(.)+\@(.)+\.(.)+')
return r.match(txt) and 1 or 0
def send_account_activated_message(account_email, send_to, password, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new activated
account."""
_ = gettext_set_language(ln)
sub = _("Your account on '%s' has been activated") % CFG_SITE_NAME
body = _("Your account earlier created on '%s' has been activated:") \
% CFG_SITE_NAME + '\n\n'
body += ' ' + _("Username/Email:") + " %s\n" % account_email
body += ' ' + _("Password:") + " %s\n" % ("*" * len(str(password)))
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def send_new_user_account_warning(new_account_email, send_to, password, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account
new_account_email."""
_ = gettext_set_language(ln)
sub = _("Account created on '%s'") % CFG_SITE_NAME
body = _("An account has been created for you on '%s':") % CFG_SITE_NAME + '\n\n'
body += ' ' + _("Username/Email:") + " %s\n" % new_account_email
body += ' ' + _("Password:") + " %s\n" % ("*" * len(str(password)))
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def send_account_rejected_message(new_account_email, send_to, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account
new_account_email."""
_ = gettext_set_language(ln)
sub = _("Account rejected on '%s'") % CFG_SITE_NAME
body = _("Your request for an account has been rejected on '%s':") \
% CFG_SITE_NAME + '\n\n'
body += ' ' + _("Username/Email: %s") % new_account_email + "\n"
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def send_account_deleted_message(new_account_email, send_to, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account
new_account_email."""
_ = gettext_set_language(ln)
sub = _("Account deleted on '%s'") % CFG_SITE_NAME
body = _("Your account on '%s' has been deleted:") % CFG_SITE_NAME + '\n\n'
body += ' ' + _("Username/Email:") + " %s\n" % new_account_email
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def usage(exitcode=1, msg=""):
"""Prints usage info."""
if msg:
print >> sys.stderr, "Error: %s." % msg
print >> sys.stderr
print >> sys.stderr, """Usage: %s [options]
General options:
-h, --help\t\tprint this help
-V, --version\t\tprint version number
Authentication options:
-u, --user=USER\tUser name needed to perform the administrative task
Option to administrate authorizations:
-a, --add\t\tadd default authorization settings
-c, --compile\t\tcompile firewall like role definitions (FireRole)
-r, --reset\t\treset to default settings
-D, --demo\t\tto be used with -a or -r in order to consider demo site authorizationss
""" % sys.argv[0]
sys.exit(exitcode)
def main():
"""Main function that analyzes command line input and calls whatever
is appropriate. """
## parse command line:
# set user-defined options:
options = {'user' : '', 'reset' : 0, 'compile' : 0, 'add' : 0, 'demo' : 0}
try:
opts, args = getopt.getopt(sys.argv[1:], "hVu:racD",
["help", "version", "user=",
"reset", "add", "compile", "demo"])
except getopt.GetoptError, err:
usage(1, err)
try:
for opt in opts:
if opt[0] in ("-h", "--help"):
usage(0)
elif opt[0] in ("-V", "--version"):
print __revision__
sys.exit(0)
elif opt[0] in ("-u", "--user"):
options["user"] = opt[1]
elif opt[0] in ("-r", "--reset"):
options["reset"] = 1
elif opt[0] in ("-a", "--add"):
options["add"] = 1
elif opt[0] in ("-c", "--compile"):
options["compile"] = 1
elif opt[0] in ("-D", "--demo"):
options["demo"] = 1
else:
usage(1)
if options['add'] or options['reset'] or options['compile']:
if acca.acc_get_action_id('cfgwebaccess'):
# Action exists hence authentication works :-)
options['user'] = authenticate(options['user'],
authorization_msg="WebAccess Administration",
authorization_action="cfgwebaccess")
if options['reset'] and options['demo']:
acca.acc_reset_default_settings([CFG_SITE_ADMIN_EMAIL], DEF_DEMO_USER_ROLES, DEF_DEMO_ROLES, DEF_DEMO_AUTHS)
print "Reset default demo site settings."
elif options['reset']:
acca.acc_reset_default_settings([CFG_SITE_ADMIN_EMAIL])
print "Reset default settings."
elif options['add'] and options['demo']:
acca.acc_add_default_settings([CFG_SITE_ADMIN_EMAIL], DEF_DEMO_USER_ROLES, DEF_DEMO_ROLES, DEF_DEMO_AUTHS)
print "Added default demo site settings."
elif options['add']:
acca.acc_add_default_settings([CFG_SITE_ADMIN_EMAIL])
print "Added default settings."
if options['compile']:
repair_role_definitions()
print "Compiled firewall like role definitions."
else:
usage(1, "You must specify at least one command")
except StandardError, e:
usage(e)
return
### okay, here we go:
if __name__ == '__main__':
main()
diff --git a/modules/webaccess/web/admin/webaccessadmin.py b/modules/webaccess/web/admin/webaccessadmin.py
index ca087852f..ec7a74f3e 100644
--- a/modules/webaccess/web/admin/webaccessadmin.py
+++ b/modules/webaccess/web/admin/webaccessadmin.py
@@ -1,345 +1,348 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio WebAccess Administrator Interface."""
__revision__ = "$Id$"
from invenio.config import CFG_SITE_LANG
import invenio.webaccessadmin_lib as wal
from invenio.access_control_config import CFG_ACC_EMPTY_ROLE_DEFINITION_SRC
# reload(wal)
# from invenio.webaccessadmin_lib import index
index = wal.index
def rolearea(req, grep='', ln=CFG_SITE_LANG):
"""create the role area menu page."""
return wal.perform_rolearea(req=req, grep=grep)
def actionarea(req, grep='', ln=CFG_SITE_LANG):
"""create the role area menu page."""
return wal.perform_actionarea(req=req, grep=grep)
+def managerobotlogin(req, robot_name='', new_pwd1='', new_pwd2='', login_method='', timeout='', referer='', ip='', action='', confirm=0, email='', groups='', nickname='', json_assertion='', url_only=0, ln=CFG_SITE_LANG):
+ """Manage robot login keys and test URL."""
+ return wal.perform_managerobotlogin(req=req, robot_name=robot_name, new_pwd1=new_pwd1, new_pwd2=new_pwd2, login_method=login_method, timeout=timeout, referer=referer, ip=ip, action=action, confirm=confirm, email=email, groups=groups, nickname=nickname, json_assertion=json_assertion, url_only=url_only)
def userarea(req, email_user_pattern='', ln=CFG_SITE_LANG):
"""create the user area menu page. """
return wal.perform_userarea(req=req,
email_user_pattern=email_user_pattern)
def listgroups(req, ln=CFG_SITE_LANG):
return wal.perform_listgroups(req=req)
def resetarea(req, ln=CFG_SITE_LANG):
"""create the role area menu page."""
return wal.perform_resetarea(req=req)
def resetdefaultsettings(req, superusers=[], confirm=0, ln=CFG_SITE_LANG):
"""create the reset default settings page. """
return wal.perform_resetdefaultsettings(req=req,
superusers=superusers,
confirm=confirm)
def adddefaultsettings(req, superusers=[], confirm=0, ln=CFG_SITE_LANG):
"""create the add default settings page. """
return wal.perform_adddefaultsettings(req=req,
superusers=superusers,
confirm=confirm)
def manageaccounts(req, mtype='', content='', confirm=0, ln=CFG_SITE_LANG):
"""enable, disable and edit accounts"""
return wal.perform_manageaccounts(req=req, mtype=mtype, content=content, confirm=confirm)
def modifyaccountstatus(req, userID, email_user_pattern='', limit_to=-1, maxpage=25, page=1, callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""enable or disable account"""
return wal.perform_modifyaccountstatus(req=req, userID=userID, email_user_pattern=email_user_pattern, limit_to=limit_to, maxpage=maxpage, page=page, callback=callback, confirm=confirm)
def modifypreferences(req, userID, login_method='', callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""modify the preferences of an account"""
return wal.perform_modifypreferences(req=req, userID=userID, login_method=login_method, callback=callback, confirm=confirm)
def modifylogindata(req, userID, nickname='', email='', password='', callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""modify the email/password of an account"""
return wal.perform_modifylogindata(req=req, userID=userID, nickname=nickname, email=email, password=password, callback=callback, confirm=confirm)
def rejectaccount(req, userID, email_user_pattern='', limit_to=-1, maxpage=25, page=1, callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""Set account inactive, delete it and send email to the owner."""
return wal.perform_rejectaccount(req=req, userID=userID, email_user_pattern=email_user_pattern, limit_to=limit_to, maxpage=maxpage, page=page, callback=callback, confirm=confirm)
def deleteaccount(req, userID, callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""delete account"""
return wal.perform_deleteaccount(req=req, userID=userID, callback=callback, confirm=confirm)
def createaccount(req, email='', password='', callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""create account"""
return wal.perform_createaccount(req=req, email=email, password=password, callback=callback, confirm=confirm)
def editaccount(req, userID, mtype='', content='', callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""edit account. """
return wal.perform_editaccount(req=req, userID=userID, mtype=mtype, content=content, callback=callback, confirm=confirm)
def becomeuser(req, userID='', callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""edit account. """
return wal.perform_becomeuser(req=req, userID=userID, callback=callback, confirm=confirm)
def modifyaccounts(req, email_user_pattern='', limit_to=-1, maxpage=25, page=1, callback='yes', confirm=0, ln=CFG_SITE_LANG):
"""Modify accounts. """
return wal.perform_modifyaccounts(req=req, email_user_pattern=email_user_pattern, limit_to=limit_to, maxpage=maxpage, page=page, callback=callback,confirm=confirm)
def delegate_startarea(req, ln=CFG_SITE_LANG):
"""add info here"""
return wal.perform_delegate_startarea(req=req)
def delegate_adminsetup(req, id_role_admin=0, id_role_delegate=0, confirm=0, ln=CFG_SITE_LANG):
"""add info here"""
return wal.perform_delegate_adminsetup(req=req,
id_role_admin=id_role_admin,
id_role_delegate=id_role_delegate,
confirm=confirm)
def delegate_adduserrole(req, id_role=0, email_user_pattern='', id_user=0, confirm=0, ln=CFG_SITE_LANG):
"""add info here"""
return wal.perform_delegate_adduserrole(req=req,
id_role=id_role,
email_user_pattern=email_user_pattern,
id_user=id_user,
confirm=confirm)
def delegate_deleteuserrole(req, id_role=0, id_user=0, confirm=0, ln=CFG_SITE_LANG):
"""add info here"""
return wal.perform_delegate_deleteuserrole(req=req,
id_role=id_role,
id_user=id_user,
confirm=confirm)
def addrole(req, name_role='', description='put description here.', firerole_def_src=CFG_ACC_EMPTY_ROLE_DEFINITION_SRC, confirm=0, ln=CFG_SITE_LANG):
"""form to add a new role with these values:
name_role - name of the new role
description - optional description of the role
firerole_def_src - optional firerole like definition
"""
return wal.perform_addrole(req=req,
name_role=name_role,
description=description,
firerole_def_src=firerole_def_src,
confirm=confirm)
def modifyrole(req, id_role='0', name_role='', description='put description here.', firerole_def_src='', modified='0', confirm=0, ln=CFG_SITE_LANG):
"""form to add a new role with these values:
name_role - name of the new role
description - optional description of the role
firerole_def_src - optional firerole like definition
"""
return wal.perform_modifyrole(req=req,
id_role=id_role,
name_role=name_role,
description=description,
firerole_def_src=firerole_def_src,
modified=modified,
confirm=confirm)
def deleterole(req, id_role="0", confirm=0, ln=CFG_SITE_LANG):
"""select a role and show all connected information,
users - users that can access the role.
actions - actions with possible authorizations."""
return wal.perform_deleterole(req=req,
id_role=id_role,
confirm=confirm)
def showroledetails(req, id_role='0', ln=CFG_SITE_LANG):
"""show the details of a role."""
return wal.perform_showroledetails(req=req,
id_role=id_role)
def showactiondetails(req, id_action="0", ln=CFG_SITE_LANG):
"""show the details of an action. """
return wal.perform_showactiondetails(req=req,
id_action=id_action)
def showuserdetails(req, id_user="0", ln=CFG_SITE_LANG):
"""show the details of an action. """
return wal.perform_showuserdetails(req=req,
id_user=id_user)
def adduserrole(req, id_role='0', email_user_pattern='', id_user='0', confirm=0, ln=CFG_SITE_LANG):
"""create connection between user and role.
id_role - id of the role to add user to
email_user_pattern - search for users using this pattern
id_user - id of user to add to the role. """
return wal.perform_adduserrole(req=req,
id_role=id_role,
email_user_pattern=email_user_pattern,
id_user=id_user,
confirm=confirm)
def addroleuser(req, email_user_pattern='', id_user='0', id_role='0', confirm=0, ln=CFG_SITE_LANG):
"""create connection between user and role.
email_user_pattern - search for users using this pattern
id_user - id of user to add to the role.
id_role - id of the role to add user to. """
return wal.perform_addroleuser(req=req,
email_user_pattern=email_user_pattern,
id_user=id_user,
id_role=id_role,
confirm=confirm)
def deleteuserrole(req, id_role='0', id_user='0', reverse=0, confirm=0, ln=CFG_SITE_LANG):
"""delete connection between role and user.
id_role - id of role to disconnect
id_user - id of user to disconnect. """
return wal.perform_deleteuserrole(req=req,
id_role=id_role,
id_user=id_user,
reverse=reverse,
confirm=confirm)
def addauthorization(req, id_role="0", id_action="0", reverse="0", confirm=0, **keywords):
""" form to add new connection between user and role:
id_role - role to connect
id_action - action to connect
reverse - role or action first? """
return wal.perform_addauthorization(req=req,
id_role=id_role,
id_action=id_action,
reverse=reverse,
confirm=confirm,
**keywords)
def deleteroleaction(req, id_role="0", id_action="0", reverse=0, confirm=0, ln=CFG_SITE_LANG):
"""delete all connections between a role and an action.
id_role - id of the role
id_action - id of the action
reverse - 0: ask for role first
1: ask for action first"""
return wal.perform_deleteroleaction(req=req,
id_role=id_role,
id_action=id_action,
reverse=reverse,
confirm=confirm)
def modifyauthorizations(req, id_role="0", id_action="0", reverse=0, confirm=0, sel='', errortext='', authids=[], ln=CFG_SITE_LANG):
"""given ids of a role and an action, show all possible action combinations
with checkboxes and allow user to access other functions.
id_role - id of the role
id_action - id of the action
reverse - 0: ask for role first
1: ask for action first
sel - which button and modification that is selected
errortext - text to print when no connection exist between role and action
authids - ids of checked checkboxes """
return wal.perform_modifyauthorizations(req=req,
id_role=id_role,
id_action=id_action,
reverse=reverse,
confirm=confirm,
sel=sel,
authids=authids)
def simpleauthorization(req, id_role=0, id_action=0, ln=CFG_SITE_LANG):
"""show a page with simple overview of authorizations between a
connected role and action. """
return wal.perform_simpleauthorization(req=req,
id_role=id_role,
id_action=id_action)
def showroleusers(req, id_role=0, ln=CFG_SITE_LANG):
"""show a page with simple overview of a role and connected users. """
return wal.perform_showroleusers(req=req,
id_role=id_role)
diff --git a/modules/webbasket/lib/webbasket_webinterface.py b/modules/webbasket/lib/webbasket_webinterface.py
index 3e1edcee5..3b1b58ac9 100644
--- a/modules/webbasket/lib/webbasket_webinterface.py
+++ b/modules/webbasket/lib/webbasket_webinterface.py
@@ -1,1573 +1,1573 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebBasket Web Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
from invenio import webinterface_handler_config as apache
import os
from invenio.config import CFG_SITE_URL, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS, \
CFG_SITE_SECURE_URL, CFG_PREFIX, CFG_SITE_LANG
from invenio.messages import gettext_set_language
from invenio.webpage import page
from invenio.webuser import getUid, page_not_authorized, isGuestUser
from invenio.webbasket import \
check_user_can_comment, \
check_sufficient_rights, \
perform_request_display, \
perform_request_search, \
create_guest_warning_box, \
create_basket_navtrail, \
perform_request_write_note, \
perform_request_save_note, \
perform_request_delete_note, \
perform_request_add_group, \
perform_request_edit, \
perform_request_edit_topic, \
perform_request_list_public_baskets, \
perform_request_unsubscribe, \
perform_request_subscribe, \
perform_request_display_public, \
perform_request_write_public_note, \
perform_request_save_public_note, \
delete_record, \
move_record, \
perform_request_add, \
perform_request_create_basket, \
perform_request_delete, \
wash_topic, \
wash_group, \
perform_request_export_xml, \
page_start
from invenio.webbasket_config import CFG_WEBBASKET_CATEGORIES, \
CFG_WEBBASKET_ACTIONS, \
CFG_WEBBASKET_SHARE_LEVELS
from invenio.webbasket_dblayer import get_basket_name, \
get_max_user_rights_on_basket
from invenio.urlutils import get_referer, redirect_to_url, make_canonical_urlargd
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.webstat import register_customevent
from invenio.errorlib import register_exception
from invenio.webuser import collect_user_info
from invenio.webcomment import check_user_can_attach_file_to_comments
from invenio.access_control_engine import acc_authorize_action
try:
from invenio.fckeditor_invenio_connector import FCKeditorConnectorInvenio
fckeditor_available = True
except ImportError, e:
fckeditor_available = False
from invenio.bibdocfile import stream_file
class WebInterfaceBasketCommentsFiles(WebInterfaceDirectory):
"""Handle upload and access to files for comments in WebBasket.
The upload is currently only available through the FCKeditor.
"""
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for getting
and putting attachments) Eg:
/yourbaskets/attachments/get/31/652/5/file/myfile.pdf
/yourbaskets/attachments/get/31/552/5/image/myfigure.png
bskid/recid/uid/
/yourbaskets/attachments/put/31/550/
bskid/recid
"""
if component == 'get' and len(path) > 4:
bskid = path[0] # Basket id
recid = path[1] # Record id
uid = path[2] # uid of the submitter
file_type = path[3] # file, image, flash or media (as
# defined by FCKeditor)
if file_type in ['file', 'image', 'flash', 'media']:
file_name = '/'.join(path[4:]) # the filename
def answer_get(req, form):
"""Accessing files attached to comments."""
form['file'] = file_name
form['type'] = file_type
form['uid'] = uid
form['recid'] = recid
form['bskid'] = bskid
return self._get(req, form)
return answer_get, []
elif component == 'put' and len(path) > 1:
bskid = path[0] # Basket id
recid = path[1] # Record id
def answer_put(req, form):
"""Attaching file to a comment."""
form['recid'] = recid
form['bskid'] = bskid
return self._put(req, form)
return answer_put, []
# All other cases: file not found
return None, []
def _get(self, req, form):
"""
Returns a file attached to a comment.
A file is attached to a comment of a record of a basket, by a
user (who is the author of the comment), and is of a certain
type (file, image, etc). Therefore these 5 values are part of
the URL. Eg:
CFG_SITE_URL/yourbaskets/attachments/get/31/91/5/file/myfile.pdf
bskid/recid/uid
"""
argd = wash_urlargd(form, {'file': (str, None),
'type': (str, None),
'uid': (int, 0),
'bskid': (int, 0),
'recid': (int, 0)})
_ = gettext_set_language(argd['ln'])
# Can user view this basket & record & comment, i.e. can user
# access its attachments?
#uid = getUid(req)
user_info = collect_user_info(req)
rights = get_max_user_rights_on_basket(argd['uid'], argd['bskid'])
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
- if user_info['email'] == 'guest' and not user_info['apache_user']:
+ if user_info['email'] == 'guest':
# Ask to login
target = '/youraccount/login' + \
make_canonical_urlargd({'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif not(check_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['READITM'])):
return page_not_authorized(req, "../", \
text = _("You are not authorized to view this attachment"))
if not argd['file'] is None:
# Prepare path to file on disk. Normalize the path so that
# ../ and other dangerous components are removed.
path = os.path.abspath(CFG_PREFIX + '/var/data/baskets/comments/' + \
str(argd['bskid']) + '/' + str(argd['recid']) + '/' + \
str(argd['uid']) + '/' + argd['type'] + '/' + \
argd['file'])
# Check that we are really accessing attachements
# directory, for the declared basket and record.
if path.startswith(CFG_PREFIX + '/var/data/baskets/comments/' + \
str(argd['bskid']) + '/' + str(argd['recid'])) and \
os.path.exists(path):
return stream_file(req, path)
# Send error 404 in all other cases
return apache.HTTP_NOT_FOUND
def _put(self, req, form):
"""
Process requests received from FCKeditor to upload files, etc.
URL eg:
CFG_SITE_URL/yourbaskets/attachments/put/31/91/
bskid/recid/
"""
if not fckeditor_available:
return
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0)})
uid = getUid(req)
# URL where the file can be fetched after upload
user_files_path = '%(CFG_SITE_URL)s/yourbaskets/attachments/get/%(bskid)s/%(recid)i/%(uid)s' % \
{'uid': uid,
'recid': argd['recid'],
'bskid': argd['bskid'],
'CFG_SITE_URL': CFG_SITE_URL}
# Path to directory where uploaded files are saved
user_files_absolute_path = '%(CFG_PREFIX)s/var/data/baskets/comments/%(bskid)s/%(recid)s/%(uid)s' % \
{'uid': uid,
'recid': argd['recid'],
'bskid': argd['bskid'],
'CFG_PREFIX': CFG_PREFIX}
# Create a Connector instance to handle the request
conn = FCKeditorConnectorInvenio(form, recid=argd['recid'], uid=uid,
allowed_commands=['QuickUpload'],
allowed_types = ['File', 'Image', 'Flash', 'Media'],
user_files_path = user_files_path,
user_files_absolute_path = user_files_absolute_path)
# Check that user can
# 1. is logged in
# 2. comment records of this basket (to simplify, we use
# WebComment function to check this, even if it is not
# entirely adequate)
# 3. attach files
user_info = collect_user_info(req)
(auth_code, dummy) = check_user_can_attach_file_to_comments(user_info, argd['recid'])
- if user_info['email'] == 'guest' and not user_info['apache_user']:
+ if user_info['email'] == 'guest':
# 1. User is guest: must login prior to upload
data = conn.sendUploadResults(1, '', '', 'Please login before uploading file.')
if not user_info['precached_usebaskets']:
data = conn.sendUploadResults(1, '', '', 'Sorry, you are not allowed to use WebBasket')
elif not check_user_can_comment(uid, argd['bskid']):
# 2. User cannot edit comment of this basket
data = conn.sendUploadResults(1, '', '', 'Sorry, you are not allowed to submit files')
elif auth_code:
# 3. User cannot submit
data = conn.sendUploadResults(1, '', '', 'Sorry, you are not allowed to submit files.')
else:
# Process the upload and get the response
data = conn.doResponse()
# Transform the headers into something ok for mod_python
for header in conn.headers:
if not header is None:
if header[0] == 'Content-Type':
req.content_type = header[1]
else:
req.headers_out[header[0]] = header[1]
# Send our response
req.send_http_header()
req.write(data)
class WebInterfaceYourBasketsPages(WebInterfaceDirectory):
"""Defines the set of /yourbaskets pages."""
_exports = ['',
'display_item',
'display',
'search',
'write_note',
'save_note',
'delete_note',
'add',
'delete',
'modify',
'edit',
'edit_topic',
'create_basket',
'display_public',
'list_public_baskets',
'subscribe',
'unsubscribe',
'write_public_note',
'save_public_note',
'attachments']
attachments = WebInterfaceBasketCommentsFiles()
def index(self, req, dummy):
"""Index page."""
redirect_to_url(req, '%s/yourbaskets/display?%s' % (CFG_SITE_URL, req.args))
def display_item(self, req, dummy):
"""Legacy URL redirection."""
redirect_to_url(req, '%s/yourbaskets/display?%s' % (CFG_SITE_URL, req.args))
def display(self, req, form):
"""Display basket interface."""
#import rpdb2; rpdb2.start_embedded_debugger('password', fAllowRemote=True)
argd = wash_urlargd(form, {'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (str, ""),
'group': (int, 0),
'bskid': (int, 0),
'recid': (int, 0),
'bsk_to_sort': (int, 0),
'sort_by_title': (str, ""),
'sort_by_date': (str, ""),
'of': (str, "hb"),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/display",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/display%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings, navtrail) = perform_request_display(uid=uid,
selected_category=argd['category'],
selected_topic=argd['topic'],
selected_group_id=argd['group'],
selected_bskid=argd['bskid'],
selected_recid=argd['recid'],
of=argd['of'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
# register event in webstat
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["display", "", user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
if argd['of'] != 'hb':
page_start(req, of=argd['of'])
if argd['of'].startswith('x'):
xml = perform_request_export_xml(body)
return xml
return page(title = _("Display baskets"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def search(self, req, form):
"""Search baskets interface."""
argd = wash_urlargd(form, {'category': (str, ""),
'topic': (str, ""),
'group': (int, 0),
'p': (str, ""),
'b': (str, ""),
'n': (int, 0),
'of': (str, "hb"),
'verbose': (int, 0),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/search",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/search%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings, navtrail) = perform_request_search(uid=uid,
selected_category=argd['category'],
selected_topic=argd['topic'],
selected_group_id=argd['group'],
p=argd['p'],
b=argd['b'],
n=argd['n'],
#format=argd['of'],
ln=argd['ln'])
# register event in webstat
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["search", "", user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Search baskets"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def write_note(self, req, form):
"""Write a comment (just interface for writing)"""
argd = wash_urlargd(form, {'category': (str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (str, ""),
'group': (int, 0),
'bskid': (int, 0),
'recid': (int, 0),
'cmtid': (int, 0),
'of' : (str, ''),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/write_note",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/write_note%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings, navtrail) = perform_request_write_note(uid=uid,
category=argd['category'],
topic=argd['topic'],
group_id=argd['group'],
bskid=argd['bskid'],
recid=argd['recid'],
cmtid=argd['cmtid'],
ln=argd['ln'])
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["write_note", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Add a note"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def save_note(self, req, form):
"""Save comment on record in basket"""
argd = wash_urlargd(form, {'category': (str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (str, ""),
'group': (int, 0),
'bskid': (int, 0),
'recid': (int, 0),
'note_title': (str, ""),
'note_body': (str, ""),
'editor_type': (str, ""),
'of': (str, ''),
'ln': (str, CFG_SITE_LANG),
'reply_to': (int, 0)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/save_note",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/save_note%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings, navtrail) = perform_request_save_note(uid=uid,
category=argd['category'],
topic=argd['topic'],
group_id=argd['group'],
bskid=argd['bskid'],
recid=argd['recid'],
note_title=argd['note_title'],
note_body=argd['note_body'],
editor_type=argd['editor_type'],
ln=argd['ln'],
reply_to=argd['reply_to'])
# TODO: do not stat event if save was not succussful
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["save_note", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Display item and notes"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def delete_note(self, req, form):
"""Delete a comment
@param bskid: id of basket (int)
@param recid: id of record (int)
@param cmtid: id of comment (int)
@param category: category (see webbasket_config) (str)
@param topic: nb of topic currently displayed (int)
@param group: id of group baskets currently displayed (int)
@param ln: language"""
argd = wash_urlargd(form, {'category': (str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (str, ""),
'group': (int, 0),
'bskid': (int, 0),
'recid': (int, 0),
'cmtid': (int, 0),
'of' : (str, ''),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/delete_note",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/delete_note%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/display%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings, navtrail) = perform_request_delete_note(uid=uid,
category=argd['category'],
topic=argd['topic'],
group_id=argd['group'],
bskid=argd['bskid'],
recid=argd['recid'],
cmtid=argd['cmtid'],
ln=argd['ln'])
# TODO: do not stat event if delete was not succussful
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
user_info = collect_user_info(req)
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["delete_note", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Display item and notes"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def add(self, req, form):
"""Add records to baskets.
@param recid: list of records to add
@param colid: in case of external collections, the id of the collection the records belong to
@param bskids: list of baskets to add records to. if not provided,
will return a page where user can select baskets
@param referer: URL of the referring page
@param new_basket_name: add record to new basket
@param new_topic_name: new basket goes into new topic
@param create_in_topic: # of topic to put basket into
@param ln: language"""
# TODO: apply a maximum limit of items (100) that can be added to a basket
# at once. Also see the build_search_url function of websearch_..._searcher.py
# for the "rg" GET variable.
argd = wash_urlargd(form, {'recid': (list, []),
'category': (str, ""),
'bskid': (int, 0),
'colid': (int, 0),
'es_title': (str, ""),
'es_desc': (str, ""),
'es_url': (str, ""),
'note_body': (str, ""),
'editor_type': (str, ""),
'b': (str, ""),
'copy': (int, 0),
'referer': (str, ""),
"of" : (str, ''),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/add",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/add%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
if not argd['referer']:
argd['referer'] = get_referer(req)
(body, warnings, navtrail) = perform_request_add(uid=uid,
recids=argd['recid'],
colid=argd['colid'],
bskid=argd['bskid'],
es_title=argd['es_title'],
es_desc=argd['es_desc'],
es_url=argd['es_url'],
note_body=argd['note_body'],
editor_type=argd['editor_type'],
category=argd['category'],
b=argd['b'],
copy=argd['copy'],
referer=argd['referer'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
# register event in webstat
bskid = argd['bskid']
basket_str = "%s (%s)" % (get_basket_name(bskid), bskid)
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["add", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _('Add to basket'),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def delete(self, req, form):
"""Delete basket interface"""
argd = wash_urlargd(form, {'bskid': (int, -1),
'confirmed': (int, 0),
'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (str, ""),
'group': (int, 0),
'of' : (str, ''),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/delete",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/delete%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings)=perform_request_delete(uid=uid,
bskid=argd['bskid'],
confirmed=argd['confirmed'],
category=argd['category'],
selected_topic=argd['topic'],
selected_group_id=argd['group'],
ln=argd['ln'])
if argd['confirmed']:
if argd['category'] == CFG_WEBBASKET_CATEGORIES['PRIVATE']:
argd['topic'] = wash_topic(uid, argd['topic'])[0]
elif argd['category'] == CFG_WEBBASKET_CATEGORIES['GROUP']:
argd['group'] = wash_group(uid, argd['group'])[0]
url = """%s/yourbaskets/display?category=%s&topic=%s&group=%i&ln=%s""" % \
(CFG_SITE_URL, argd['category'], argd['topic'], argd['group'], argd['ln'])
redirect_to_url(req, url)
else:
navtrail = ''\
'%s'
navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
bskid=argd['bskid'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["delete", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Delete a basket"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def modify(self, req, form):
"""Modify basket content interface (reorder, suppress record, etc.)"""
argd = wash_urlargd(form, {'action': (str, ""),
'bskid': (int, -1),
'recid': (int, 0),
'category': (str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (str, ""),
'group': (int, 0),
'of' : (str, ''),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/modify",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/modify%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
url = CFG_SITE_URL
url += '/yourbaskets/display?category=%s&topic=%s&group=%i&bskid=%i&ln=%s' % \
(argd['category'], argd['topic'], argd['group'], argd['bskid'], argd['ln'])
if argd['action'] == CFG_WEBBASKET_ACTIONS['DELETE']:
delete_record(uid, argd['bskid'], argd['recid'])
redirect_to_url(req, url)
elif argd['action'] == CFG_WEBBASKET_ACTIONS['UP']:
move_record(uid, argd['bskid'], argd['recid'], argd['action'])
redirect_to_url(req, url)
elif argd['action'] == CFG_WEBBASKET_ACTIONS['DOWN']:
move_record(uid, argd['bskid'], argd['recid'], argd['action'])
redirect_to_url(req, url)
elif argd['action'] == CFG_WEBBASKET_ACTIONS['COPY']:
title = _("Copy record to basket")
referer = get_referer(req)
(body, warnings, navtrail) = perform_request_add(uid=uid,
recids=argd['recid'],
copy=True,
referer=referer,
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
else:
title = ''
body = ''
warnings = [('WRN_WEBBASKET_UNDEFINED_ACTION',)]
navtrail = ''\
'%s'
navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
bskid=argd['bskid'],
ln=argd['ln'])
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["modify", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = title,
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def edit(self, req, form):
"""Edit basket interface"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'groups': (list, []),
'topic': (str, ""),
'add_group': (str, ""),
'group_cancel': (str, ""),
'submit': (str, ""),
'cancel': (str, ""),
'delete': (str, ""),
'new_name': (str, ""),
'new_topic': (str, ""),
'new_topic_name': (str, ""),
'new_group': (str, ""),
'external': (str, ""),
'of' : (str, ''),
'ln': (str, CFG_SITE_LANG)})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/edit",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/edit%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
if argd['cancel']:
url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%s&ln=%s'
url %= (CFG_WEBBASKET_CATEGORIES['PRIVATE'], argd['topic'],
argd['ln'])
redirect_to_url(req, url)
elif argd['delete']:
url = CFG_SITE_URL
url += '/yourbaskets/delete?bskid=%i&category=%s&topic=%s&ln=%s' % \
(argd['bskid'], CFG_WEBBASKET_CATEGORIES['PRIVATE'],
argd['topic'], argd['ln'])
redirect_to_url(req, url)
elif argd['add_group'] and not(argd['new_group']):
body = perform_request_add_group(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
ln=argd['ln'])
warnings = []
elif (argd['add_group'] and argd['new_group']) or argd['group_cancel']:
if argd['add_group']:
perform_request_add_group(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
group_id=argd['new_group'],
ln=argd['ln'])
(body, warnings) = perform_request_edit(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
ln=argd['ln'])
elif argd['submit']:
(body, warnings) = perform_request_edit(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
new_name=argd['new_name'],
new_topic=argd['new_topic'],
new_topic_name=argd['new_topic_name'],
groups=argd['groups'],
external=argd['external'],
ln=argd['ln'])
if argd['new_topic'] != "-1":
argd['topic'] = argd['new_topic']
url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%s&ln=%s' % \
(CFG_WEBBASKET_CATEGORIES['PRIVATE'],
argd['topic'], argd['ln'])
redirect_to_url(req, url)
else:
(body, warnings) = perform_request_edit(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
ln=argd['ln'])
navtrail = ''\
'%s'
navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(
uid=uid,
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
topic=argd['topic'],
group=0,
bskid=argd['bskid'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["edit", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Edit basket"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def edit_topic(self, req, form):
"""Edit topic interface"""
argd = wash_urlargd(form, {'topic': (str, ""),
'submit': (str, ""),
'cancel': (str, ""),
'delete': (str, ""),
'new_name': (str, ""),
'of' : (str, ''),
'ln': (str, CFG_SITE_LANG)})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/edit",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/edit_topic%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
if argd['cancel']:
url = CFG_SITE_URL + '/yourbaskets/display?category=%s&ln=%s'
url %= (CFG_WEBBASKET_CATEGORIES['PRIVATE'], argd['ln'])
#url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%s&ln=%s'
#url %= (CFG_WEBBASKET_CATEGORIES['PRIVATE'], argd['topic'],
# argd['ln'])
redirect_to_url(req, url)
elif argd['delete']:
url = CFG_SITE_URL
url += '/yourbaskets/delete?bskid=%i&category=%s&topic=%s&ln=%s' % \
(argd['bskid'], CFG_WEBBASKET_CATEGORIES['PRIVATE'],
argd['topic'], argd['ln'])
redirect_to_url(req, url)
elif argd['submit']:
f = open("/tmp/skata", "w")
f.write("submit: " + argd['topic'] + " |+| " + argd['new_name'] + "\n")
f.close()
(body, warnings) = perform_request_edit_topic(uid=uid,
topic=argd['topic'],
new_name=argd['new_name'],
ln=argd['ln'])
#url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%s&ln=%s' % \
# (CFG_WEBBASKET_CATEGORIES['PRIVATE'],
# argd['topic'], argd['ln'])
url = CFG_SITE_URL + '/yourbaskets/display?category=%s&ln=%s' % \
(CFG_WEBBASKET_CATEGORIES['PRIVATE'], argd['ln'])
redirect_to_url(req, url)
else:
f = open("/tmp/skata", "w")
f.write("normal: " + argd['topic'] + " |+| " + argd['new_name'] + "\n")
f.close()
(body, warnings) = perform_request_edit_topic(uid=uid,
topic=argd['topic'],
ln=argd['ln'])
navtrail = ''\
'%s'
navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = ""
#navtrail_end = create_basket_navtrail(
# uid=uid,
# category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
# topic=argd['topic'],
# group=0,
# ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
# register event in webstat
#basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
#if user_info['email']:
# user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
#else:
# user_str = ""
#try:
# register_customevent("baskets", ["edit", basket_str, user_str])
#except:
# register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Edit topic"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def create_basket(self, req, form):
"""Create basket interface"""
argd = wash_urlargd(form, {'new_basket_name': (str, ""),
'new_topic_name' : (str, ""),
'create_in_topic': (str, "-1"),
'topic' : (str, ""),
'of' : (str, ''),
'ln' : (str, CFG_SITE_LANG)})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/create_basket",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/create_basket%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
_ = gettext_set_language(argd['ln'])
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
if argd['new_basket_name'] and \
(argd['new_topic_name'] or argd['create_in_topic'] != "-1"):
topic = perform_request_create_basket(
uid=uid,
new_basket_name=argd['new_basket_name'],
new_topic_name=argd['new_topic_name'],
create_in_topic=argd['create_in_topic'],
ln=argd['ln'])
# register event in webstat
basket_str = "%s ()" % argd['new_basket_name']
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["create_basket", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%s&ln=%s'
url %= (CFG_WEBBASKET_CATEGORIES['PRIVATE'], topic, argd['ln'])
redirect_to_url(req, url)
else:
(body, warnings) = perform_request_create_basket(uid=uid,
new_basket_name=argd['new_basket_name'],
new_topic_name=argd['new_topic_name'],
create_in_topic=argd['create_in_topic'],
topic=argd['topic'],
ln=argd['ln'])
navtrail = '%s'
navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
return page(title = _("Create basket"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def display_public(self, req, form):
"""Display a public basket"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0),
'of': (str, "hb"),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/display",
navmenuid = 'yourbaskets')
user_info = collect_user_info(req)
if not argd['bskid']:
(body, warnings, navtrail) = perform_request_list_public_baskets(uid)
title = _('List of public baskets')
# register event in webstat
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["list_public_baskets", "", user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
else:
(body, warnings, navtrail) = perform_request_display_public(uid=uid,
selected_bskid=argd['bskid'],
selected_recid=argd['recid'],
of=argd['of'],
ln=argd['ln'])
title = _('Public basket')
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["display_public", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
if argd['of'] == 'xm':
page_start(req, of=argd['of'])
return perform_request_export_xml(body)
return page(title = title,
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def list_public_baskets(self, req, form):
"""List of public baskets interface."""
argd = wash_urlargd(form, {'limit': (int, 1),
'sort': (str, 'name'),
'asc': (int, 1),
'of': (str, ''),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
return page_not_authorized(req, "../yourbaskets/list_public_baskets",
navmenuid = 'yourbaskets')
user_info = collect_user_info(req)
nb_views_show = acc_authorize_action(user_info, 'runwebstatadmin')
nb_views_show_p = not(nb_views_show[0])
(body, warnings, navtrail) = perform_request_list_public_baskets(uid,
argd['limit'],
argd['sort'],
argd['asc'],
nb_views_show_p,
argd['ln'])
return page(title = _("List of public baskets"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def subscribe(self, req, form):
"""Subscribe to a basket pseudo-interface."""
argd = wash_urlargd(form, {'bskid': (int, 0),
'of': (str, 'hb'),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
return page_not_authorized(req, "../yourbaskets/subscribe",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/subscribe%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
if not argd['bskid']:
(body, warnings, navtrail) = perform_request_list_public_baskets(uid)
title = _('List of public baskets')
else:
# TODO: Take care of XML output as shown below
#req.content_type = "text/xml"
#req.send_http_header()
#return perform_request_display_public(bskid=argd['bskid'], of=argd['of'], ln=argd['ln'])
(subscribe_warnings_html, subscribe_warnings) = perform_request_subscribe(uid, argd['bskid'], argd['ln'])
(body, warnings, navtrail) = perform_request_display_public(uid=uid,
selected_bskid=argd['bskid'],
selected_recid=0,
of=argd['of'],
ln=argd['ln'])
warnings.extend(subscribe_warnings)
body = subscribe_warnings_html + body
title = _('Public basket')
return page(title = title,
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def unsubscribe(self, req, form):
"""Unsubscribe from basket pseudo-interface."""
argd = wash_urlargd(form, {'bskid': (int, 0),
'of': (str, 'hb'),
'ln': (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
return page_not_authorized(req, "../yourbaskets/unsubscribe",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/unsubscribe%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
if not argd['bskid']:
(body, warnings, navtrail) = perform_request_list_public_baskets(uid)
title = _('List of public baskets')
else:
# TODO: Take care of XML output as shown below
#req.content_type = "text/xml"
#req.send_http_header()
#return perform_request_display_public(bskid=argd['bskid'], of=argd['of'], ln=argd['ln'])
(unsubscribe_warnings_html, unsubscribe_warnings) = perform_request_unsubscribe(uid, argd['bskid'], argd['ln'])
(body, warnings, navtrail) = perform_request_display_public(uid=uid,
selected_bskid=argd['bskid'],
selected_recid=0,
of=argd['of'],
ln=argd['ln'])
warnings.extend(unsubscribe_warnings)
body = unsubscribe_warnings_html + body
title = _('Public basket')
return page(title = title,
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
def write_public_note(self, req, form):
"""Write a comment (just interface for writing)"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0),
'cmtid': (int, 0),
'of' : (str, ''),
'ln' : (str, CFG_SITE_LANG)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/write_public_note",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/write_public_note%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings, navtrail) = perform_request_write_public_note(uid=uid,
bskid=argd['bskid'],
recid=argd['recid'],
cmtid=argd['cmtid'],
ln=argd['ln'])
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["write_public_note", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Add a note"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def save_public_note(self, req, form):
"""Save comment on record in basket"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0),
'note_title': (str, ""),
'note_body': (str, ""),
'editor_type': (str, ""),
'of': (str, ''),
'ln': (str, CFG_SITE_LANG),
'reply_to': (str, 0)})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/save_public_note",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourbaskets/save_public_note%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
if not user_info['precached_usebaskets']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use baskets."))
(body, warnings, navtrail) = perform_request_save_public_note(uid=uid,
bskid=argd['bskid'],
recid=argd['recid'],
note_title=argd['note_title'],
note_body=argd['note_body'],
editor_type=argd['editor_type'],
ln=argd['ln'],
reply_to=argd['reply_to'])
# TODO: do not stat event if save was not succussful
# register event in webstat
basket_str = "%s (%d)" % (get_basket_name(argd['bskid']), argd['bskid'])
if user_info['email']:
user_str = "%s (%d)" % (user_info['email'], user_info['uid'])
else:
user_str = ""
try:
register_customevent("baskets", ["save_public_note", basket_str, user_str])
except:
register_exception(suffix="Do the webstat tables exists? Try with 'webstatadmin --load-config'")
return page(title = _("Display item and notes"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'],
navtrail_append_title_p = 0)
diff --git a/modules/webcomment/lib/webcomment_webinterface.py b/modules/webcomment/lib/webcomment_webinterface.py
index 450ebf0c1..d99d8b782 100644
--- a/modules/webcomment/lib/webcomment_webinterface.py
+++ b/modules/webcomment/lib/webcomment_webinterface.py
@@ -1,818 +1,818 @@
# -*- coding: utf-8 -*-
## Comments and reviews for records.
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Comments and reviews for records: web interface """
__lastupdated__ = """$Date$"""
__revision__ = """$Id$"""
import cgi
from invenio.webcomment import check_recID_is_in_range, \
perform_request_display_comments_or_remarks, \
perform_request_add_comment_or_remark, \
perform_request_vote, \
perform_request_report, \
subscribe_user_to_discussion, \
unsubscribe_user_from_discussion, \
get_user_subscription_to_discussion, \
check_user_can_attach_file_to_comments, \
check_user_can_view_comments, \
check_user_can_send_comments, \
check_user_can_view_comment, \
query_get_comment
from invenio.config import \
CFG_TMPDIR, \
CFG_SITE_LANG, \
CFG_SITE_URL, \
CFG_PREFIX, \
CFG_WEBCOMMENT_ALLOW_COMMENTS,\
CFG_WEBCOMMENT_ALLOW_REVIEWS, \
CFG_WEBCOMMENT_USE_JSMATH_IN_COMMENTS
from invenio.webuser import getUid, page_not_authorized, isGuestUser, collect_user_info
from invenio.webpage import page, pageheaderonly, pagefooteronly
from invenio.search_engine import create_navtrail_links, \
guess_primary_collection_of_a_record, \
get_colID
from invenio.urlutils import redirect_to_url, \
make_canonical_urlargd
from invenio.errorlib import register_exception
from invenio.messages import gettext_set_language
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import \
mail_cookie_create_authorize_action, \
mail_cookie_create_common, \
mail_cookie_check_common, \
InvenioWebAccessMailCookieDeletedError, \
InvenioWebAccessMailCookieError
from invenio.webcomment_config import \
CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE, \
CFG_WEBCOMMENT_MAX_ATTACHED_FILES
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
try:
from invenio.fckeditor_invenio_connector import FCKeditorConnectorInvenio
fckeditor_available = True
except ImportError, e:
fckeditor_available = False
import os
from invenio import webinterface_handler_config as apache
from invenio.bibdocfile import \
stream_file, \
decompose_file, \
propose_next_docname
class WebInterfaceCommentsPages(WebInterfaceDirectory):
"""Defines the set of /comments pages."""
_exports = ['', 'display', 'add', 'vote', 'report', 'index', 'attachments',
'subscribe', 'unsubscribe']
def __init__(self, recid=-1, reviews=0):
self.recid = recid
self.discussion = reviews # 0:comments, 1:reviews
self.attachments = WebInterfaceCommentsFiles(recid, reviews)
def index(self, req, form):
"""
Redirects to display function
"""
return self.display(req, form)
def display(self, req, form):
"""
Display comments (reviews if enabled) associated with record having id recid where recid>0.
This function can also be used to display remarks associated with basket having id recid where recid<-99.
@param ln: language
@param recid: record id, integer
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param voted: boolean, active if user voted for a review, see vote function
@param reported: int, active if user reported a certain comment/review, see report function
@param reviews: boolean, enabled for reviews, disabled for comments
@param subscribed: int, 1 if user just subscribed to discussion, -1 if unsubscribed
@return the full html page.
"""
argd = wash_urlargd(form, {'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'voted': (int, -1),
'reported': (int, -1),
'subscribed': (int, 0),
'cmtgrp': (list, ["latest"]) # 'latest' is now a reserved group/round name
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
can_send_comments = False
(auth_code, auth_msg) = check_user_can_send_comments(user_info, self.recid)
if not auth_code:
can_send_comments = True
can_attach_files = False
(auth_code, auth_msg) = check_user_can_attach_file_to_comments(user_info, self.recid)
- if not auth_code and (user_info['email'] != 'guest' or user_info['apache_user']):
+ if not auth_code and (user_info['email'] != 'guest'):
can_attach_files = True
subscription = get_user_subscription_to_discussion(self.recid, uid)
if subscription == 1:
user_is_subscribed_to_discussion = True
user_can_unsubscribe_from_discussion = True
elif subscription == 2:
user_is_subscribed_to_discussion = True
user_can_unsubscribe_from_discussion = False
else:
user_is_subscribed_to_discussion = False
user_can_unsubscribe_from_discussion = False
#display_comment_rounds = [cmtgrp for cmtgrp in argd['cmtgrp'] if cmtgrp.isdigit() or cmtgrp == "all" or cmtgrp == "-1"]
display_comment_rounds = argd['cmtgrp']
check_warnings = []
(ok, problem) = check_recID_is_in_range(self.recid, check_warnings, argd['ln'])
if ok:
(body, errors, warnings) = perform_request_display_comments_or_remarks(req=req, recID=self.recid,
display_order=argd['do'],
display_since=argd['ds'],
nb_per_page=argd['nb'],
page=argd['p'],
ln=argd['ln'],
voted=argd['voted'],
reported=argd['reported'],
subscribed=argd['subscribed'],
reviews=self.discussion,
uid=uid,
can_send_comments=can_send_comments,
can_attach_files=can_attach_files,
user_is_subscribed_to_discussion=user_is_subscribed_to_discussion,
user_can_unsubscribe_from_discussion=user_can_unsubscribe_from_discussion,
display_comment_rounds=display_comment_rounds
)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
'%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id in ['comments', 'reviews'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
if navtrail:
navtrail += ' > '
navtrail += ''% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += ''
navtrail += ' > %s' % (self.discussion==1 and _("Reviews") or _("Comments"))
jsmathheader = ''
if CFG_WEBCOMMENT_USE_JSMATH_IN_COMMENTS:
jsmathheader = """
"""
jqueryheader = '''
''' % {'CFG_SITE_URL': CFG_SITE_URL}
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
metaheaderadd = jsmathheader + jqueryheader,
req=req,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
else:
return page(title=_("Record Not Found"),
body=problem,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
warnings=check_warnings, errors=[],
navmenuid='search')
# Return the same page wether we ask for /record/123 or /record/123/
__call__ = index
def add(self, req, form):
"""
Add a comment (review) to record with id recid where recid>0
Also works for adding a remark to basket with id recid where recid<-99
@param ln: languange
@param recid: record id
@param action: 'DISPLAY' to display add form
'SUBMIT' to submit comment once form is filled
'REPLY' to reply to an already existing comment
@param msg: the body of the comment/review or remark
@param score: star score of the review
@param note: title of the review
@param comid: comment id, needed for replying
@param editor_type: the type of editor used for submitting the
comment: 'textarea', 'fckeditor'.
@param subscribe: if set, subscribe user to receive email
notifications when new comment are added to
this discussion
@return the full html page.
"""
argd = wash_urlargd(form, {'action': (str, "DISPLAY"),
'msg': (str, ""),
'note': (str, ''),
'score': (int, 0),
'comid': (int, -1),
'editor_type': (str, ""),
'subscribe': (str, ""),
'cookie': (str, "")
})
_ = gettext_set_language(argd['ln'])
actions = ['DISPLAY', 'REPLY', 'SUBMIT']
uid = getUid(req)
# Is site ready to accept comments?
if uid == -1 or (not CFG_WEBCOMMENT_ALLOW_COMMENTS and not CFG_WEBCOMMENT_ALLOW_REVIEWS):
return page_not_authorized(req, "../comments/add",
navmenuid='search')
# Is user allowed to post comment?
user_info = collect_user_info(req)
(auth_code_1, auth_msg_1) = check_user_can_view_comments(user_info, self.recid)
(auth_code_2, auth_msg_2) = check_user_can_send_comments(user_info, self.recid)
if isGuestUser(uid):
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
# Save user's value in cookie, so that these "POST"
# parameters are not lost during login process
msg_cookie = mail_cookie_create_common('comment_msg',
{'msg': argd['msg'],
'note': argd['note'],
'score': argd['score'],
'editor_type': argd['editor_type'],
'subscribe': argd['subscribe']},
onetime=True)
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri'] + '&cookie=' + msg_cookie}, {})
return redirect_to_url(req, target, norobot=True)
elif (auth_code_1 or auth_code_2):
return page_not_authorized(req, "../", \
text = auth_msg_1 + auth_msg_2)
user_info = collect_user_info(req)
can_attach_files = False
(auth_code, auth_msg) = check_user_can_attach_file_to_comments(user_info, self.recid)
- if not auth_code and (user_info['email'] != 'guest' or user_info['apache_user']):
+ if not auth_code and (user_info['email'] != 'guest'):
can_attach_files = True
warning_msgs = []
added_files = {}
if can_attach_files:
# User is allowed to attach files. Process the files
file_too_big = False
formfields = form.get('commentattachment[]', [])
if not hasattr(formfields, "__getitem__"): # A single file was uploaded
formfields = [formfields]
for formfield in formfields[:CFG_WEBCOMMENT_MAX_ATTACHED_FILES]:
if hasattr(formfield, "filename") and formfield.filename:
filename = formfield.filename
dir_to_open = os.path.join(CFG_TMPDIR, 'webcomment', str(uid))
try:
assert(dir_to_open.startswith(CFG_TMPDIR))
except AssertionError:
register_exception(req=req,
prefix='User #%s tried to upload file to forbidden location: %s' \
% (uid, dir_to_open))
if not os.path.exists(dir_to_open):
try:
os.makedirs(dir_to_open)
except:
register_exception(req=req, alert_admin=True)
## Before saving the file to disc, wash the filename (in particular
## washing away UNIX and Windows (e.g. DFS) paths):
filename = os.path.basename(filename.split('\\')[-1])
filename = filename.strip()
if filename != "":
# Check that file does not already exist
n = 1
while os.path.exists(os.path.join(dir_to_open, filename)):
basedir, name, extension = decompose_file(filename)
new_name = propose_next_docname(name)
filename = new_name + extension
fp = open(os.path.join(dir_to_open, filename), "w")
# FIXME: temporary, waiting for wsgi handler to be
# fixed. Once done, read chunk by chunk
## while formfield.file:
## fp.write(formfield.file.read(10240))
fp.write(formfield.file.read())
fp.close()
# Isn't this file too big?
file_size = os.path.getsize(os.path.join(dir_to_open, filename))
if CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE > 0 and \
file_size > CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE:
os.remove(os.path.join(dir_to_open, filename))
# One file is too big: record that,
# dismiss all uploaded files and re-ask to
# upload again
file_too_big = True
warning_msgs.append(('WRN_WEBCOMMENT_MAX_FILE_SIZE_REACHED', cgi.escape(filename), str(file_size/1024) + 'KB', str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/1024) + 'KB'))
else:
added_files[filename] = os.path.join(dir_to_open, filename)
if file_too_big:
# One file was too big. Removed all uploaded filed
for filepath in added_files.items():
try:
os.remove(filepath)
except:
# File was already removed or does not exist?
pass
client_ip_address = req.remote_ip
check_warnings = []
(ok, problem) = check_recID_is_in_range(self.recid, check_warnings, argd['ln'])
if ok:
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req,
self.recid,
argd['ln'])
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid))
if navtrail:
navtrail += ' > '
navtrail += ''% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += ''
navtrail += '> %s' % (CFG_SITE_URL,
self.recid,
self.discussion==1 and 'reviews' or 'comments',
argd['ln'],
self.discussion==1 and _('Reviews') or _('Comments'))
if argd['action'] not in actions:
argd['action'] = 'DISPLAY'
if not argd['msg']:
# User had to login in-between, so retrieve msg
# from cookie
try:
(kind, cookie_argd) = mail_cookie_check_common(argd['cookie'],
delete=True)
argd.update(cookie_argd)
except InvenioWebAccessMailCookieDeletedError, e:
return redirect_to_url(req, CFG_SITE_URL + '/record/' + \
str(self.recid) + (self.discussion==1 and \
'/reviews' or '/comments'))
except InvenioWebAccessMailCookieError, e:
# Invalid or empty cookie: continue
pass
subscribe = False
if argd['subscribe'] and \
get_user_subscription_to_discussion(self.recid, uid) == 0:
# User is not already subscribed, and asked to subscribe
subscribe = True
(body, errors, warnings) = perform_request_add_comment_or_remark(recID=self.recid,
ln=argd['ln'],
uid=uid,
action=argd['action'],
msg=argd['msg'],
note=argd['note'],
score=argd['score'],
reviews=self.discussion,
comID=argd['comid'],
client_ip_address=client_ip_address,
editor_type=argd['editor_type'],
can_attach_files=can_attach_files,
subscribe=subscribe,
req=req,
attached_files=added_files,
warnings=warning_msgs)
if self.discussion:
title = _("Add Review")
else:
title = _("Add Comment")
jqueryheader = '''
''' % {'CFG_SITE_URL': CFG_SITE_URL}
return page(title=title,
body=body,
navtrail=navtrail,
uid=uid,
language=CFG_SITE_LANG,
verbose=1,
errors=errors,
warnings=warnings,
req=req,
navmenuid='search',
metaheaderadd=jqueryheader)
# id not in range
else:
return page(title=_("Record Not Found"),
body=problem,
uid=uid,
verbose=1,
req=req,
warnings=check_warnings, errors=[],
navmenuid='search')
def vote(self, req, form):
"""
Vote positively or negatively for a comment/review.
@param comid: comment/review id
@param com_value: +1 to vote positively
-1 to vote negatively
@param recid: the id of the record the comment/review is associated with
@param ln: language
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param referer: http address of the calling function to redirect to (refresh)
@param reviews: boolean, enabled for reviews, disabled for comments
"""
argd = wash_urlargd(form, {'comid': (int, -1),
'com_value': (int, 0),
'recid': (int, -1),
'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'referer': (str, None)
})
client_ip_address = req.remote_ip
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = perform_request_vote(argd['comid'], client_ip_address, argd['com_value'], uid)
if argd['referer']:
argd['referer'] += "?ln=%s&do=%s&ds=%s&nb=%s&p=%s&voted=%s&" % (
argd['ln'], argd['do'], argd['ds'], argd['nb'], argd['p'], success)
redirect_to_url(req, argd['referer'])
else:
#Note: sent to comments display
referer = "%s/record/%s/%s?&ln=%s&voted=1"
referer %= (CFG_SITE_URL, self.recid, self.discussion == 1 and 'reviews' or 'comments', argd['ln'])
redirect_to_url(req, referer)
def report(self, req, form):
"""
Report a comment/review for inappropriate content
@param comid: comment/review id
@param recid: the id of the record the comment/review is associated with
@param ln: language
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param referer: http address of the calling function to redirect to (refresh)
@param reviews: boolean, enabled for reviews, disabled for comments
"""
argd = wash_urlargd(form, {'comid': (int, -1),
'recid': (int, -1),
'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'referer': (str, None)
})
client_ip_address = req.remote_ip
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
- if (auth_code and not user_info['apache_user']) or user_info['email'] == 'guest':
+ if auth_code or user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = perform_request_report(argd['comid'], client_ip_address, uid)
if argd['referer']:
argd['referer'] += "?ln=%s&do=%s&ds=%s&nb=%s&p=%s&reported=%s&" % (argd['ln'], argd['do'], argd['ds'], argd['nb'], argd['p'], str(success))
redirect_to_url(req, argd['referer'])
else:
#Note: sent to comments display
referer = "%s/record/%s/%s/display?ln=%s&voted=1"
referer %= (CFG_SITE_URL, self.recid, self.discussion==1 and 'reviews' or 'comments', argd['ln'])
redirect_to_url(req, referer)
def subscribe(self, req, form):
"""
Subscribe current user to receive email notification when new
comments are added to current discussion.
"""
argd = wash_urlargd(form, {'referer': (str, None)})
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
if isGuestUser(uid):
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = subscribe_user_to_discussion(self.recid, uid)
display_url = "%s/record/%s/comments/display?subscribed=%s&ln=%s" % \
(CFG_SITE_URL, self.recid, str(success), argd['ln'])
redirect_to_url(req, display_url)
def unsubscribe(self, req, form):
"""
Unsubscribe current user from current discussion.
"""
argd = wash_urlargd(form, {'referer': (str, None)})
user_info = collect_user_info(req)
uid = getUid(req)
if isGuestUser(uid):
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
success = unsubscribe_user_from_discussion(self.recid, uid)
display_url = "%s/record/%s/comments/display?subscribed=%s&ln=%s" % \
(CFG_SITE_URL, self.recid, str(-success), argd['ln'])
redirect_to_url(req, display_url)
class WebInterfaceCommentsFiles(WebInterfaceDirectory):
"""Handle upload and access to files for comments.
The upload is currently only available through the FCKeditor.
"""
#_exports = ['put'] # 'get' is handled by _lookup(..)
def __init__(self, recid=-1, reviews=0):
self.recid = recid
self.discussion = reviews # 0:comments, 1:reviews
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for getting
and putting attachments) Eg:
CFG_SITE_URL/record/5953/comments/attachments/get/652/myfile.pdf
"""
if component == 'get' and len(path) > 1:
comid = path[0] # comment ID
file_name = '/'.join(path[1:]) # the filename
def answer_get(req, form):
"""Accessing files attached to comments."""
form['file'] = file_name
form['comid'] = comid
return self._get(req, form)
return answer_get, []
# All other cases: file not found
return None, []
def _get(self, req, form):
"""
Returns a file attached to a comment.
Example:
CFG_SITE_URL/record/5953/comments/attachments/get/652/myfile.pdf
where 652 is the comment ID
"""
argd = wash_urlargd(form, {'file': (str, None),
'comid': (int, 0)})
_ = gettext_set_language(argd['ln'])
# Can user view this record, i.e. can user access its
# attachments?
uid = getUid(req)
user_info = collect_user_info(req)
# Check that user can view record, and its comments (protected
# with action "viewcomment")
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
# Does comment exist?
if not query_get_comment(argd['comid']):
req.status = apache.HTTP_NOT_FOUND
return page(title=_("Page Not Found"),
body=_('The requested comment could not be found'),
req=req)
# Check that user can view this particular comment, protected
# using its own restriction
(auth_code, auth_msg) = check_user_can_view_comment(user_info, argd['comid'])
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,
ln=argd['ln'])
if not argd['file'] is None:
# Prepare path to file on disk. Normalize the path so that
# ../ and other dangerous components are removed.
path = os.path.abspath(CFG_PREFIX + '/var/data/comments/' + \
str(self.recid) + '/' + str(argd['comid']) + \
'/' + argd['file'])
# Check that we are really accessing attachements
# directory, for the declared record.
if path.startswith(CFG_PREFIX + '/var/data/comments/' + \
str(self.recid)) and \
os.path.exists(path):
return stream_file(req, path)
# Send error 404 in all other cases
req.status = apache.HTTP_NOT_FOUND
return page(title=_("Page Not Found"),
body=_('The requested file could not be found'),
req=req,
language=argd['ln'])
## def put(self, req, form):
## """
## Process requests received from FCKeditor to upload files, etc.
## """
## if not fckeditor_available:
## return
## uid = getUid(req)
## # URL where the file can be fetched after upload
## user_files_path = '%(CFG_SITE_URL)s/record/%(recid)i/comments/attachments/get/%(uid)s' % \
## {'uid': uid,
## 'recid': self.recid,
## 'CFG_SITE_URL': CFG_SITE_URL}
## # Path to directory where uploaded files are saved
## user_files_absolute_path = '%(CFG_PREFIX)s/var/data/comments/%(recid)s/%(uid)s' % \
## {'uid': uid,
## 'recid': self.recid,
## 'CFG_PREFIX': CFG_PREFIX}
## # Create a Connector instance to handle the request
## conn = FCKeditorConnectorInvenio(form, recid=self.recid, uid=uid,
## allowed_commands=['QuickUpload'],
## allowed_types = ['File', 'Image', 'Flash', 'Media'],
## user_files_path = user_files_path,
## user_files_absolute_path = user_files_absolute_path)
## # Check that user can upload attachments for comments.
## user_info = collect_user_info(req)
## (auth_code, auth_msg) = check_user_can_attach_file_to_comments(user_info, self.recid)
-## if user_info['email'] == 'guest' and not user_info['apache_user']:
+## if user_info['email'] == 'guest':
## # User is guest: must login prior to upload
## data = conn.sendUploadResults(1, '', '', 'Please login before uploading file.')
## elif auth_code:
## # User cannot submit
## data = conn.sendUploadResults(1, '', '', 'Sorry, you are not allowed to submit files.')
## else:
## # Process the upload and get the response
## data = conn.doResponse()
## # Transform the headers into something ok for mod_python
## for header in conn.headers:
## if not header is None:
## if header[0] == 'Content-Type':
## req.content_type = header[1]
## else:
## req.headers_out[header[0]] = header[1]
## # Send our response
## req.send_http_header()
## req.write(data)
diff --git a/modules/websearch/lib/websearch_webinterface.py b/modules/websearch/lib/websearch_webinterface.py
index db4988672..9af7a00b4 100644
--- a/modules/websearch/lib/websearch_webinterface.py
+++ b/modules/websearch/lib/websearch_webinterface.py
@@ -1,1289 +1,1279 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSearch URL handler."""
__revision__ = "$Id$"
import cgi
import os
import datetime
import time
import sys
from urllib import quote
from invenio import webinterface_handler_config as apache
#maximum number of collaborating authors etc shown in GUI
MAX_COLLAB_LIST = 10
MAX_KEYWORD_LIST = 10
MAX_VENUE_LIST = 10
#tag constants
AUTHOR_TAG = "100__a"
AUTHOR_INST_TAG = "100__u"
COAUTHOR_TAG = "700__a"
COAUTHOR_INST_TAG = "700__u"
VENUE_TAG = "909C4p"
KEYWORD_TAG = "695__a"
FKEYWORD_TAG = "6531_a"
CFG_INSPIRE_UNWANTED_KEYWORDS_START= ['talk',
'conference',
'conference proceedings',
'numerical calculations',
'experimental results',
'review',
'bibliography',
'upper limit',
'lower limit',
'tables',
'search for',
'on-shell',
'off-shell',
'formula',
'lectures',
'book',
'thesis']
CFG_INSPIRE_UNWANTED_KEYWORDS_MIDDLE = ['GeV',
'((']
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from invenio.config import \
CFG_SITE_URL, \
CFG_SITE_NAME, \
CFG_CACHEDIR, \
CFG_SITE_LANG, \
CFG_SITE_SECURE_URL, \
CFG_BIBRANK_SHOW_DOWNLOAD_STATS, \
CFG_WEBSEARCH_INSTANT_BROWSE_RSS, \
CFG_WEBSEARCH_RSS_TTL, \
CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS, \
CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE, \
CFG_WEBDIR, \
CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS, \
CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS, \
CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL, \
CFG_WEBSEARCH_USE_ALEPH_SYSNOS, \
CFG_WEBSEARCH_RSS_I18N_COLLECTIONS, \
CFG_INSPIRE_SITE
from invenio.dbquery import Error
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import redirect_to_url, make_canonical_urlargd, drop_default_urlargd
from invenio.webuser import getUid, page_not_authorized, get_user_preferences, \
- collect_user_info, http_check_credentials, logoutUser, isUserSuperAdmin
+ collect_user_info, logoutUser, isUserSuperAdmin
from invenio.websubmit_webinterface import WebInterfaceFilesPages
from invenio.webcomment_webinterface import WebInterfaceCommentsPages
from invenio.bibcirculation_webinterface import WebInterfaceHoldingsPages
from invenio.webpage import page, create_error_box
from invenio.messages import gettext_set_language
from invenio.search_engine import check_user_can_view_record, \
collection_reclist_cache, \
collection_restricted_p, \
create_similarly_named_authors_link_box, \
get_colID, \
get_coll_i18nname, \
get_fieldvalues, \
get_fieldvalues_alephseq_like, \
get_most_popular_field_values, \
get_mysql_recid_from_aleph_sysno, \
guess_primary_collection_of_a_record, \
page_end, \
page_start, \
perform_request_cache, \
perform_request_log, \
perform_request_search, \
restricted_collection_cache
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
from invenio.bibformat import format_records
from invenio.bibformat_engine import get_output_formats
from invenio.websearch_webcoll import mymkdir, get_collection
from invenio.intbitset import intbitset
from invenio.bibupload import find_record_from_sysno
from invenio.bibrank_citation_searcher import get_cited_by_list
from invenio.bibrank_downloads_indexer import get_download_weight_total
from invenio.search_engine_summarizer import summarize_records
from invenio.errorlib import register_exception
from invenio.bibedit_webinterface import WebInterfaceEditPages
from invenio.bibeditmulti_webinterface import WebInterfaceMultiEditPages
from invenio.bibmerge_webinterface import WebInterfaceMergePages
import invenio.template
websearch_templates = invenio.template.load('websearch')
search_results_default_urlargd = websearch_templates.search_results_default_urlargd
search_interface_default_urlargd = websearch_templates.search_interface_default_urlargd
try:
output_formats = [output_format['attrs']['code'].lower() for output_format in \
get_output_formats(with_attributes=True).values()]
except KeyError:
output_formats = ['xd', 'xm', 'hd', 'hb', 'hs', 'hx']
output_formats.extend(['hm', 't', 'h'])
def wash_search_urlargd(form):
"""
Create canonical search arguments from those passed via web form.
"""
argd = wash_urlargd(form, search_results_default_urlargd)
if argd.has_key('as'):
argd['aas'] = argd['as']
del argd['as']
# Sometimes, users pass ot=245,700 instead of
# ot=245&ot=700. Normalize that.
ots = []
for ot in argd['ot']:
ots += ot.split(',')
argd['ot'] = ots
# We can either get the mode of function as
# action=, or by setting action_browse or
# action_search.
if argd['action_browse']:
argd['action'] = 'browse'
elif argd['action_search']:
argd['action'] = 'search'
else:
if argd['action'] not in ('browse', 'search'):
argd['action'] = 'search'
del argd['action_browse']
del argd['action_search']
return argd
class WebInterfaceUnAPIPages(WebInterfaceDirectory):
""" Handle /unapi set of pages."""
_exports = ['']
def __call__(self, req, form):
argd = wash_urlargd(form, {
'id' : (int, 0),
'format' : (str, '')})
formats_dict = get_output_formats(True)
formats = {}
for format in formats_dict.values():
if format['attrs']['visibility']:
formats[format['attrs']['code'].lower()] = format['attrs']['content_type']
del formats_dict
if argd['id'] and argd['format']:
## Translate back common format names
format = {
'nlm' : 'xn',
'marcxml' : 'xm',
'dc' : 'xd',
'endnote' : 'xe',
'mods' : 'xo'
}.get(argd['format'], argd['format'])
if format in formats:
redirect_to_url(req, '%s/record/%s/export/%s' % (CFG_SITE_URL, argd['id'], format))
else:
raise apache.SERVER_RETURN, apache.HTTP_NOT_ACCEPTABLE
elif argd['id']:
return websearch_templates.tmpl_unapi(formats, identifier=argd['id'])
else:
return websearch_templates.tmpl_unapi(formats)
index = __call__
class WebInterfaceAuthorPages(WebInterfaceDirectory):
""" Handle /author/Doe%2C+John etc set of pages."""
_exports = ['author']
def __init__(self, authorname=''):
"""Constructor."""
self.authorname = authorname
def _lookup(self, component, path):
"""This handler parses dynamic URLs (/author/John+Doe)."""
return WebInterfaceAuthorPages(component), path
def __call__(self, req, form):
"""Serve the page in the given language."""
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG), 'verbose': (int, 0) })
ln = argd['ln']
verbose = argd['verbose']
req.argd = argd #needed since perform_req_search
# start page
req.content_type = "text/html"
req.send_http_header()
uid = getUid(req)
page_start(req, "hb", "", "", ln, uid)
#wants to check it in case of no results
self.authorname = self.authorname.replace("+"," ")
if not self.authorname:
return websearch_templates.tmpl_author_information(req, {}, self.authorname,
0, {},
{}, {}, {}, {}, ln)
#let's see what takes time..
time1 = time.time()
genstart = time1
time2 = time.time()
#search the publications by this author
pubs = perform_request_search(req=req, p=self.authorname, f="exactauthor")
#get most frequent authors of these pubs
popular_author_tuples = get_most_popular_field_values(pubs, (AUTHOR_TAG, COAUTHOR_TAG))
authors = []
for (auth, frequency) in popular_author_tuples:
if len(authors) < MAX_COLLAB_LIST:
authors.append(auth)
time1 = time.time()
if verbose == 9:
req.write(" popularized authors: "+str(time1-time2)+" ")
#and publication venues
venuetuples = get_most_popular_field_values(pubs, (VENUE_TAG))
time2 = time.time()
if verbose == 9:
req.write(" venues: "+str(time2-time1)+" ")
#and keywords
kwtuples = get_most_popular_field_values(pubs, (KEYWORD_TAG, FKEYWORD_TAG), count_repetitive_values=False)
if CFG_INSPIRE_SITE:
# filter kw tuples against unwanted keywords:
kwtuples_filtered = ()
for (kw, num) in kwtuples:
kwlower = kw.lower()
kwlower_unwanted = False
for unwanted_keyword in CFG_INSPIRE_UNWANTED_KEYWORDS_START:
if kwlower.startswith(unwanted_keyword):
kwlower_unwanted = True # unwanted keyword found
break
for unwanted_keyword in CFG_INSPIRE_UNWANTED_KEYWORDS_MIDDLE:
if unwanted_keyword in kwlower:
kwlower_unwanted = True # unwanted keyword found
break
if not kwlower_unwanted:
kwtuples_filtered += ((kw, num),)
kwtuples = kwtuples_filtered
time1 = time.time()
if verbose == 9:
req.write(" keywords: "+str(time1-time2)+" ")
#construct a simple list of tuples that contains keywords that appear more than once
#moreover, limit the length of the list to MAX_KEYWORD_LIST
kwtuples = kwtuples[0:MAX_KEYWORD_LIST]
vtuples = venuetuples[0:MAX_VENUE_LIST]
#remove the author in question from authors: they are associates
if (authors.count(self.authorname) > 0):
authors.remove(self.authorname)
authors = authors[0:MAX_COLLAB_LIST] #cut extra
time2 = time.time()
if verbose == 9:
req.write(" misc: "+str(time2-time1)+" ")
#a dict. keys: affiliations, values: lists of publications
author_aff_pubs = self.get_institute_pub_dict(pubs)
time1 = time.time()
if verbose == 9:
req.write(" affiliations: "+str(time1-time2)+" ")
totaldownloads = 0
if CFG_BIBRANK_SHOW_DOWNLOAD_STATS:
#find out how many times these records have been downloaded
recsloads = {}
recsloads = get_download_weight_total(recsloads, pubs)
#sum up
for k in recsloads.keys():
totaldownloads = totaldownloads + recsloads[k]
#get cited by..
citedbylist = get_cited_by_list(pubs)
time1 = time.time()
if verbose == 9:
req.write(" citedby: "+str(time1-time2)+" ")
#finally all stuff there, call the template
websearch_templates.tmpl_author_information(req, pubs, self.authorname,
totaldownloads, author_aff_pubs,
citedbylist, kwtuples, authors, vtuples, ln)
time1 = time.time()
#cited-by summary
out = summarize_records(intbitset(pubs), 'hcs', ln, self.authorname, 'exactauthor', req)
time2 = time.time()
if verbose == 9:
req.write(" summarizer: "+str(time2-time1)+" ")
req.write(out)
simauthbox = create_similarly_named_authors_link_box(self.authorname)
req.write(simauthbox)
if verbose == 9:
req.write(" all: "+str(time.time()-genstart)+" ")
return page_end(req, 'hb', ln)
def get_institute_pub_dict(self, recids):
"""return a dictionary consisting of institute -> list of publications"""
author_aff_pubs = {} #the dictionary to be built
for recid in recids:
#iterate all so that we get first author's intitute
#if this the first author OR
#"his" institute if he is an affliate author
affus = [] #list of insts from the given record
mainauthors = get_fieldvalues(recid, AUTHOR_TAG)
mainauthor = " "
if mainauthors:
mainauthor = mainauthors[0]
if (mainauthor == self.authorname):
affus = get_fieldvalues(recid, AUTHOR_INST_TAG)
else:
#search for coauthors..
coauthor_field_lines = []
coauthorfield_content = get_fieldvalues_alephseq_like(recid, \
COAUTHOR_TAG[:3])
if coauthorfield_content:
coauthor_field_lines = coauthorfield_content.split("\n")
for line in coauthor_field_lines:
if line.count(self.authorname) > 0:
#get affilitions .. the correct ones are $$+code
code = COAUTHOR_INST_TAG[-1]
myparts = line.split("$$")
for part in myparts:
if part and part[0] == code:
myaff = part[1:]
affus.append(myaff)
break
#if this is empty, add a dummy " " value
if (affus == []):
affus = [" "]
for a in affus:
#add in author_aff_pubs
if (author_aff_pubs.has_key(a)):
tmp = author_aff_pubs[a]
tmp.append(recid)
author_aff_pubs[a] = tmp
else:
author_aff_pubs[a] = [recid]
return author_aff_pubs
index = __call__
class WebInterfaceRecordPages(WebInterfaceDirectory):
""" Handling of a /record/ URL fragment """
_exports = ['', 'files', 'reviews', 'comments', 'usage',
'references', 'export', 'citations', 'holdings', 'edit',
'keywords', 'multiedit', 'merge', 'plots']
#_exports.extend(output_formats)
def __init__(self, recid, tab, format=None):
self.recid = recid
self.tab = tab
self.format = format
self.files = WebInterfaceFilesPages(self.recid)
self.reviews = WebInterfaceCommentsPages(self.recid, reviews=1)
self.comments = WebInterfaceCommentsPages(self.recid)
self.usage = self
self.references = self
self.keywords = self
self.holdings = WebInterfaceHoldingsPages(self.recid)
self.citations = self
self.plots = self
self.export = WebInterfaceRecordExport(self.recid, self.format)
self.edit = WebInterfaceEditPages(self.recid)
self.merge = WebInterfaceMergePages(self.recid)
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
argd['tab'] = self.tab
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
# Return the same page wether we ask for /record/123 or /record/123/
index = __call__
class WebInterfaceRecordRestrictedPages(WebInterfaceDirectory):
""" Handling of a /record-restricted/ URL fragment """
_exports = ['', 'files', 'reviews', 'comments', 'usage',
'references', 'export', 'citations', 'holdings', 'edit',
'keywords', 'multiedit', 'merge', 'plots']
#_exports.extend(output_formats)
def __init__(self, recid, tab, format=None):
self.recid = recid
self.tab = tab
self.format = format
self.files = WebInterfaceFilesPages(self.recid)
self.reviews = WebInterfaceCommentsPages(self.recid, reviews=1)
self.comments = WebInterfaceCommentsPages(self.recid)
self.usage = self
self.references = self
self.keywords = self
self.holdings = WebInterfaceHoldingsPages(self.recid)
self.citations = self
self.plots = self
self.export = WebInterfaceRecordExport(self.recid, self.format)
self.edit = WebInterfaceEditPages(self.recid)
self.merge = WebInterfaceMergePages(self.recid)
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
user_info = collect_user_info(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
record_primary_collection = guess_primary_collection_of_a_record(self.recid)
if collection_restricted_p(record_primary_collection):
(auth_code, dummy) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=record_primary_collection)
if auth_code:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
# Keep all the arguments, they might be reused in the
# record page itself to derivate other queries
req.argd = argd
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
# Return the same page wether we ask for /record/123 or /record/123/
index = __call__
class WebInterfaceSearchResultsPages(WebInterfaceDirectory):
""" Handling of the /search URL and its sub-pages. """
_exports = ['', 'authenticate', 'cache', 'log']
def __call__(self, req, form):
""" Perform a search. """
argd = wash_search_urlargd(form)
_ = gettext_set_language(argd['ln'])
if req.method == 'POST':
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
uid = getUid(req)
user_info = collect_user_info(req)
if uid == -1:
return page_not_authorized(req, "../",
text = _("You are not authorized to view this area."),
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
if CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL == 2:
## Let's update the current collections list with all
## the restricted collections the user has rights to view.
try:
restricted_collections = user_info['precached_permitted_restricted_collections']
argd_collections = set(argd['c'])
argd_collections.update(restricted_collections)
argd['c'] = list(argd_collections)
except KeyError:
pass
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
involved_collections = set()
involved_collections.update(argd['c'])
involved_collections.add(argd['cc'])
if argd['id'] > 0:
argd['recid'] = argd['id']
if argd['idb'] > 0:
argd['recidb'] = argd['idb']
if argd['sysno']:
tmp_recid = find_record_from_sysno(argd['sysno'])
if tmp_recid:
argd['recid'] = tmp_recid
if argd['sysnb']:
tmp_recid = find_record_from_sysno(argd['sysnb'])
if tmp_recid:
argd['recidb'] = tmp_recid
if argd['recid'] > 0:
if argd['recidb'] > argd['recid']:
# Hack to check if among the restricted collections
# at least a record of the range is there and
# then if the user is not authorized for that
# collection.
recids = intbitset(xrange(argd['recid'], argd['recidb']))
restricted_collection_cache.recreate_cache_if_needed()
for collname in restricted_collection_cache.cache:
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=collname)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
coll_recids = get_collection(collname).reclist
if coll_recids & recids:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : collname})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
else:
involved_collections.add(guess_primary_collection_of_a_record(argd['recid']))
# If any of the collection requires authentication, redirect
# to the authentication form.
for coll in involved_collections:
if collection_restricted_p(coll):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# Keep all the arguments, they might be reused in the
# search_engine itself to derivate other queries
req.argd = argd
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
def cache(self, req, form):
"""Search cache page."""
argd = wash_urlargd(form, {'action': (str, 'show')})
return perform_request_cache(req, action=argd['action'])
def log(self, req, form):
"""Search log page."""
argd = wash_urlargd(form, {'date': (str, '')})
return perform_request_log(req, date=argd['date'])
def authenticate(self, req, form):
"""Restricted search results pages."""
argd = wash_search_urlargd(form)
user_info = collect_user_info(req)
for coll in argd['c'] + [argd['cc']]:
if collection_restricted_p(coll):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# Keep all the arguments, they might be reused in the
# search_engine itself to derivate other queries
req.argd = argd
uid = getUid(req)
if uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
index = __call__
class WebInterfaceLegacySearchPages(WebInterfaceDirectory):
""" Handling of the /search.py URL and its sub-pages. """
_exports = ['', ('authenticate', 'index')]
def __call__(self, req, form):
""" Perform a search. """
argd = wash_search_urlargd(form)
# We either jump into the generic search form, or the specific
# /record/... display if a recid is requested
if argd['recid'] != -1:
target = '/record/%d' % argd['recid']
del argd['recid']
else:
target = '/search'
target += make_canonical_urlargd(argd, search_results_default_urlargd)
return redirect_to_url(req, target, apache.HTTP_MOVED_PERMANENTLY)
index = __call__
# Parameters for the legacy URLs, of the form /?c=ALEPH
legacy_collection_default_urlargd = {
'as': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'aas': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'verbose': (int, 0),
'c': (str, CFG_SITE_NAME)}
class WebInterfaceSearchInterfacePages(WebInterfaceDirectory):
""" Handling of collection navigation."""
_exports = [('index.py', 'legacy_collection'),
('', 'legacy_collection'),
('search.py', 'legacy_search'),
'search', 'openurl',
'opensearchdescription', 'logout_SSO_hook']
search = WebInterfaceSearchResultsPages()
legacy_search = WebInterfaceLegacySearchPages()
def logout_SSO_hook(self, req, form):
"""Script triggered by the display of the centralized SSO logout
dialog. It logouts the user from Invenio and stream back the
expected picture."""
logoutUser(req)
req.content_type = 'image/gif'
req.encoding = None
req.filename = 'wsignout.gif'
req.headers_out["Content-Disposition"] = "inline; filename=wsignout.gif"
req.set_content_length(os.path.getsize('%s/img/wsignout.gif' % CFG_WEBDIR))
req.send_http_header()
req.sendfile('%s/img/wsignout.gif' % CFG_WEBDIR)
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for
collections and records)"""
if component == 'collection':
c = '/'.join(path)
def answer(req, form):
"""Accessing collections cached pages."""
# Accessing collections: this is for accessing the
# cached page on top of each collection.
argd = wash_urlargd(form, search_interface_default_urlargd)
# We simply return the cached page of the collection
argd['c'] = c
if not argd['c']:
# collection argument not present; display
# home collection by default
argd['c'] = CFG_SITE_NAME
# Treat `as' argument specially:
if argd.has_key('as'):
argd['aas'] = argd['as']
del argd['as']
return display_collection(req, **argd)
return answer, []
elif component == 'record' and path and path[0] == 'merge':
return WebInterfaceMergePages(), path[1:]
elif component == 'record' and path and path[0] == 'edit':
return WebInterfaceEditPages(), path[1:]
elif component == 'record' and path and path[0] == 'multiedit':
return WebInterfaceMultiEditPages(), path[1:]
elif component == 'record' or component == 'record-restricted':
try:
if CFG_WEBSEARCH_USE_ALEPH_SYSNOS:
# let us try to recognize /record/ style of URLs:
x = get_mysql_recid_from_aleph_sysno(path[0])
if x:
recid = x
else:
recid = int(path[0])
else:
recid = int(path[0])
except IndexError:
# display record #1 for URL /record without a number
recid = 1
except ValueError:
if path[0] == '':
# display record #1 for URL /record/ without a number
recid = 1
else:
# display page not found for URLs like /record/foo
return None, []
if recid <= 0:
# display page not found for URLs like /record/-5 or /record/0
return None, []
format = None
tab = ''
try:
if path[1] in ['', 'files', 'reviews', 'comments', 'usage',
'references', 'citations', 'holdings', 'edit',
'keywords', 'multiedit', 'merge', 'plots']:
tab = path[1]
elif path[1] == 'export':
tab = ''
format = path[2]
# format = None
# elif path[1] in output_formats:
# tab = ''
# format = path[1]
else:
# display page not found for URLs like /record/references
# for a collection where 'references' tabs is not visible
return None, []
except IndexError:
# Keep normal url if tabs is not specified
pass
#if component == 'record-restricted':
#return WebInterfaceRecordRestrictedPages(recid, tab, format), path[1:]
#else:
return WebInterfaceRecordPages(recid, tab, format), path[1:]
return None, []
def openurl(self, req, form):
""" OpenURL Handler."""
argd = wash_urlargd(form, websearch_templates.tmpl_openurl_accepted_args)
ret_url = websearch_templates.tmpl_openurl2invenio(argd)
if ret_url:
return redirect_to_url(req, ret_url)
else:
return redirect_to_url(req, CFG_SITE_URL)
def opensearchdescription(self, req, form):
"""OpenSearch description file"""
req.content_type = "application/opensearchdescription+xml"
req.send_http_header()
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG),
'verbose': (int, 0) })
return websearch_templates.tmpl_opensearch_description(ln=argd['ln'])
def legacy_collection(self, req, form):
"""Collection URL backward compatibility handling."""
accepted_args = dict(legacy_collection_default_urlargd)
- accepted_args.update({'referer' : (str, ''),
- 'realm' : (str, '')})
argd = wash_urlargd(form, accepted_args)
- # Apache authentication stuff
- if argd['realm']:
- http_check_credentials(req, argd['realm'])
- return redirect_to_url(req, argd['referer'] or '%s/youraccount/youradminactivities' % CFG_SITE_SECURE_URL, norobot=True)
-
- del argd['referer']
- del argd['realm']
-
# Treat `as' argument specially:
if argd.has_key('as'):
argd['aas'] = argd['as']
del argd['as']
# If we specify no collection, then we don't need to redirect
# the user, so that accessing returns the
# default collection.
if not form.has_key('c'):
return display_collection(req, **argd)
# make the collection an element of the path, and keep the
# other query elements as is. If the collection is CFG_SITE_NAME,
# however, redirect to the main URL.
c = argd['c']
del argd['c']
if c == CFG_SITE_NAME:
target = '/'
else:
target = '/collection/' + quote(c)
# Treat `as' argument specially:
# We are going to redirect, so replace `aas' by `as' visible argument:
if argd.has_key('aas'):
argd['as'] = argd['aas']
del argd['aas']
target += make_canonical_urlargd(argd, legacy_collection_default_urlargd)
return redirect_to_url(req, target)
def display_collection(req, c, aas, verbose, ln):
"""Display search interface page for collection c by looking
in the collection cache."""
_ = gettext_set_language(ln)
req.argd = drop_default_urlargd({'aas': aas, 'verbose': verbose, 'ln': ln},
search_interface_default_urlargd)
# get user ID:
try:
uid = getUid(req)
user_preferences = {}
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this collection",
navmenuid='search')
elif uid > 0:
user_preferences = get_user_preferences(uid)
except Error:
register_exception(req=req, alert_admin=True)
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=verbose, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req,
navmenuid='search')
# start display:
req.content_type = "text/html"
req.send_http_header()
# deduce collection id:
colID = get_colID(c)
if type(colID) is not int:
page_body = '
' + (_("Sorry, collection %s does not seem to exist.") % ('' + str(c) + '')) + '
'
if req.header_only:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
return page(title=_("Collection %s Not Found") % cgi.escape(c),
body=page_body,
description=(CFG_SITE_NAME + ' - ' + _("Not found") + ': ' + cgi.escape(str(c))),
keywords="%s" % CFG_SITE_NAME,
uid=uid,
language=ln,
req=req,
navmenuid='search')
# wash `aas' argument:
if not os.path.exists("%s/collections/%d/body-as=%d-ln=%s.html" % \
(CFG_CACHEDIR, colID, aas, ln)):
# nonexistent `aas' asked for, fall back to Simple Search:
aas = 0
# display collection interface page:
try:
filedesc = open("%s/collections/%d/navtrail-as=%d-ln=%s.html" % \
(CFG_CACHEDIR, colID, aas, ln), "r")
c_navtrail = filedesc.read()
filedesc.close()
except:
c_navtrail = ""
try:
filedesc = open("%s/collections/%d/body-as=%d-ln=%s.html" % \
(CFG_CACHEDIR, colID, aas, ln), "r")
c_body = filedesc.read()
filedesc.close()
except:
c_body = ""
try:
filedesc = open("%s/collections/%d/portalbox-tp-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_tp = filedesc.read()
filedesc.close()
except:
c_portalbox_tp = ""
try:
filedesc = open("%s/collections/%d/portalbox-te-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_te = filedesc.read()
filedesc.close()
except:
c_portalbox_te = ""
try:
filedesc = open("%s/collections/%d/portalbox-lt-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_lt = filedesc.read()
filedesc.close()
except:
c_portalbox_lt = ""
try:
# show help boxes (usually located in "tr", "top right")
# if users have not banned them in their preferences:
c_portalbox_rt = ""
if user_preferences.get('websearch_helpbox', 1) > 0:
filedesc = open("%s/collections/%d/portalbox-rt-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_rt = filedesc.read()
filedesc.close()
except:
c_portalbox_rt = ""
try:
filedesc = open("%s/collections/%d/last-updated-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_last_updated = filedesc.read()
filedesc.close()
except:
c_last_updated = ""
try:
title = get_coll_i18nname(c, ln)
except:
title = ""
show_title_p = True
body_css_classes = []
if c == CFG_SITE_NAME:
# Do not display title on home collection
show_title_p = False
body_css_classes.append('home')
if len(collection_reclist_cache.cache.keys()) == 1:
# if there is only one collection defined, do not print its
# title on the page as it would be displayed repetitively.
show_title_p = False
if aas == -1:
show_title_p = False
# RSS:
rssurl = CFG_SITE_URL + '/rss'
rssurl_params = []
if c != CFG_SITE_NAME:
rssurl_params.append('cc=' + quote(c))
if ln != CFG_SITE_LANG and \
c in CFG_WEBSEARCH_RSS_I18N_COLLECTIONS:
rssurl_params.append('ln=' + ln)
if rssurl_params:
rssurl += '?' + '&'.join(rssurl_params)
if 'hb' in CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS:
metaheaderadd = """
"""
else:
metaheaderadd = ''
return page(title=title,
body=c_body,
navtrail=c_navtrail,
description="%s - %s" % (CFG_SITE_NAME, c),
keywords="%s, %s" % (CFG_SITE_NAME, c),
metaheaderadd=metaheaderadd,
uid=uid,
language=ln,
req=req,
cdspageboxlefttopadd=c_portalbox_lt,
cdspageboxrighttopadd=c_portalbox_rt,
titleprologue=c_portalbox_tp,
titleepilogue=c_portalbox_te,
lastupdated=c_last_updated,
navmenuid='search',
rssurl=rssurl,
body_css_classes=body_css_classes,
show_title_p=show_title_p)
class WebInterfaceRSSFeedServicePages(WebInterfaceDirectory):
"""RSS 2.0 feed service pages."""
def __call__(self, req, form):
"""RSS 2.0 feed service."""
# Keep only interesting parameters for the search
default_params = websearch_templates.rss_default_urlargd
# We need to keep 'jrec' and 'rg' here in order to have
# 'multi-page' RSS. These parameters are not kept be default
# as we don't want to consider them when building RSS links
# from search and browse pages.
default_params.update({'jrec':(int, 1),
'rg': (int, CFG_WEBSEARCH_INSTANT_BROWSE_RSS)})
argd = wash_urlargd(form, default_params)
user_info = collect_user_info(req)
for coll in argd['c'] + [argd['cc']]:
if collection_restricted_p(coll):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# Create a standard filename with these parameters
current_url = websearch_templates.build_rss_url(argd)
cache_filename = current_url.split('/')[-1]
# In the same way as previously, add 'jrec' & 'rg'
req.content_type = "application/rss+xml"
req.send_http_header()
try:
# Try to read from cache
path = "%s/rss/%s.xml" % (CFG_CACHEDIR, cache_filename)
# Check if cache needs refresh
filedesc = open(path, "r")
last_update_time = datetime.datetime.fromtimestamp(os.stat(os.path.abspath(path)).st_mtime)
assert(datetime.datetime.now() < last_update_time + datetime.timedelta(minutes=CFG_WEBSEARCH_RSS_TTL))
c_rss = filedesc.read()
filedesc.close()
req.write(c_rss)
return
except Exception, e:
# do it live and cache
previous_url = None
if argd['jrec'] > 1:
prev_jrec = argd['jrec'] - argd['rg']
if prev_jrec < 1:
prev_jrec = 1
previous_url = websearch_templates.build_rss_url(argd,
jrec=prev_jrec)
recIDs = perform_request_search(req, of="id",
c=argd['c'], cc=argd['cc'],
p=argd['p'], f=argd['f'],
p1=argd['p1'], f1=argd['f1'],
m1=argd['m1'], op1=argd['op1'],
p2=argd['p2'], f2=argd['f2'],
m2=argd['m2'], op2=argd['op2'],
p3=argd['p3'], f3=argd['f3'],
m3=argd['m3'])
nb_found = len(recIDs)
next_url = None
if len(recIDs) >= argd['jrec'] + argd['rg']:
next_url = websearch_templates.build_rss_url(argd,
jrec=(argd['jrec'] + argd['rg']))
first_url = websearch_templates.build_rss_url(argd, jrec=1)
last_url = websearch_templates.build_rss_url(argd, jrec=nb_found-argd['rg']+1)
recIDs = recIDs[-argd['jrec']:(-argd['rg']-argd['jrec']):-1]
rss_prologue = '\n' + \
websearch_templates.tmpl_xml_rss_prologue(current_url=current_url,
previous_url=previous_url,
next_url=next_url,
first_url=first_url, last_url=last_url,
nb_found=nb_found,
jrec=argd['jrec'], rg=argd['rg']) + '\n'
req.write(rss_prologue)
rss_body = format_records(recIDs,
of='xr',
ln=argd['ln'],
user_info=user_info,
record_separator="\n",
req=req, epilogue="\n")
rss_epilogue = websearch_templates.tmpl_xml_rss_epilogue() + '\n'
req.write(rss_epilogue)
# update cache
dirname = "%s/rss" % (CFG_CACHEDIR)
mymkdir(dirname)
fullfilename = "%s/rss/%s.xml" % (CFG_CACHEDIR, cache_filename)
try:
# Remove the file just in case it already existed
# so that a bit of space is created
os.remove(fullfilename)
except OSError:
pass
# Check if there's enough space to cache the request.
if len(os.listdir(dirname)) < CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS:
try:
os.umask(022)
f = open(fullfilename, "w")
f.write(rss_prologue + rss_body + rss_epilogue)
f.close()
except IOError, v:
if v[0] == 36:
# URL was too long. Never mind, don't cache
pass
else:
raise repr(v)
index = __call__
class WebInterfaceRecordExport(WebInterfaceDirectory):
""" Handling of a /record//export/ URL fragment """
_exports = output_formats
def __init__(self, recid, format=None):
self.recid = recid
self.format = format
for output_format in output_formats:
self.__dict__[output_format] = self
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
# Check if the record belongs to a restricted primary
# collection. If yes, redirect to the authenticated URL.
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
# Return the same page wether we ask for /record/123/export/xm or /record/123/export/xm/
index = __call__
diff --git a/modules/websession/lib/webaccount.py b/modules/websession/lib/webaccount.py
index 7970e7c5b..779ac9405 100644
--- a/modules/websession/lib/webaccount.py
+++ b/modules/websession/lib/webaccount.py
@@ -1,433 +1,422 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import re
import MySQLdb
import urllib
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_CERN_SITE, \
CFG_SITE_LANG, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_VERSION, \
CFG_DATABASE_HOST, \
CFG_DATABASE_NAME
from invenio.access_control_engine import acc_authorize_action
-from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION, SUPERADMINROLE
+from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION, \
+ SUPERADMINROLE, CFG_EXTERNAL_AUTH_DEFAULT
from invenio.dbquery import run_sql
from invenio.webuser import getUid,isGuestUser, get_user_preferences, \
collect_user_info
from invenio.access_control_admin import acc_find_user_role_actions
from invenio.messages import gettext_set_language
from invenio.external_authentication import InvenioWebAccessExternalAuthError
import invenio.template
websession_templates = invenio.template.load('websession')
def perform_info(req, ln):
"""Display the main features of CDS personalize"""
out = ""
uid = getUid(req)
user_info = collect_user_info(req)
return websession_templates.tmpl_account_info(
ln = ln,
uid = uid,
guest = isGuestUser(uid),
CFG_CERN_SITE = CFG_CERN_SITE,
)
def perform_display_external_user_settings(settings, ln):
"""show external user settings which is a dictionary."""
_ = gettext_set_language(ln)
html_settings = ""
print_settings = False
settings_keys = settings.keys()
settings_keys.sort()
for key in settings_keys:
value = settings[key]
if key.startswith("EXTERNAL_") and not "HIDDEN_" in key:
print_settings = True
key = key[9:].capitalize()
html_settings += websession_templates.tmpl_external_setting(ln, key, value)
return print_settings and websession_templates.tmpl_external_user_settings(ln, html_settings) or ""
def perform_youradminactivities(user_info, ln):
"""Return text for the `Your Admin Activities' box. Analyze
whether user UID has some admin roles, and if yes, then print
suitable links for the actions he can do. If he's not admin,
print a simple non-authorized message."""
your_role_actions = acc_find_user_role_actions(user_info)
your_roles = []
your_admin_activities = []
guest = isGuestUser(user_info['uid'])
for (role, action) in your_role_actions:
if role not in your_roles:
your_roles.append(role)
if action not in your_admin_activities:
your_admin_activities.append(action)
if SUPERADMINROLE in your_roles:
for action in ("runbibedit", "cfgbibformat", "cfgbibharvest", "cfgoairepository", "cfgbibrank", "cfgbibindex", "cfgwebaccess", "cfgwebcomment", "cfgwebsearch", "cfgwebsubmiit", "cfgbibknowledge", "runbatchuploader"):
if action not in your_admin_activities:
your_admin_activities.append(action)
return websession_templates.tmpl_account_adminactivities(
ln = ln,
uid = user_info['uid'],
guest = guest,
roles = your_roles,
activities = your_admin_activities,
)
def perform_display_account(req, username, bask, aler, sear, msgs, loan, grps, sbms, appr, admn, ln):
"""Display a dynamic page that shows the user's account."""
# load the right message language
_ = gettext_set_language(ln)
uid = getUid(req)
user_info = collect_user_info(req)
#your account
if isGuestUser(uid):
user = "guest"
login = "%s/youraccount/login?ln=%s" % (CFG_SITE_SECURE_URL, ln)
accBody = _("You are logged in as guest. You may want to %(x_url_open)slogin%(x_url_close)s as a regular user.") %\
{'x_url_open': '',
'x_url_close': ''}
accBody += "
"
bask=aler=msgs= _("The %(x_fmt_open)sguest%(x_fmt_close)s users need to %(x_url_open)sregister%(x_url_close)s first") %\
{'x_fmt_open': '',
'x_fmt_close': '',
'x_url_open': '',
'x_url_close': ''}
sear= _("No queries found")
else:
user = username
accBody = websession_templates.tmpl_account_body(
ln = ln,
user = user,
)
#Display warnings if user is superuser
roles = acc_find_user_role_actions(user_info)
warnings = "0"
for role in roles:
if "superadmin" in role:
warnings = "1"
break
warning_list = superuser_account_warnings()
#check if tickets ok
tickets = (acc_authorize_action(user_info, 'runbibedit')[0] == 0)
return websession_templates.tmpl_account_page(
ln = ln,
warnings = warnings,
warning_list = warning_list,
accBody = accBody,
baskets = bask,
alerts = aler,
searches = sear,
messages = msgs,
loans = loan,
groups = grps,
submissions = sbms,
approvals = appr,
tickets = tickets,
administrative = admn
)
def superuser_account_warnings():
"""Check to see whether admin accounts have default / blank password etc. Returns a list"""
warning_array = []
#Try and connect to the mysql database with the default invenio password
try:
conn = MySQLdb.connect (host = CFG_DATABASE_HOST,
user = "root",
passwd = "my123p$ss",
db = "mysql")
conn.close()
warning_array.append("warning_mysql_password_equal_to_invenio_password")
except:
pass
#Try and connect to the invenio database with the default invenio password
try:
conn = MySQLdb.connect (host = CFG_DATABASE_HOST,
user = "invenio",
passwd = "my123p$ss",
db = CFG_DATABASE_NAME)
conn.close ()
warning_array.append("warning_invenio_password_equal_to_default")
except:
pass
#Check if the admin password is empty
res = run_sql("SELECT password, email from user where nickname = 'admin'")
res1 = run_sql("SELECT email from user where nickname = 'admin' and password = AES_ENCRYPT(%s,'')", (res[0][1], ))
for user in res1:
warning_array.append("warning_empty_admin_password")
#Check if the admin email has been changed from the default
if (CFG_SITE_ADMIN_EMAIL == "info@invenio-software.org" or CFG_SITE_SUPPORT_EMAIL == "info@invenio-software.org") and CFG_CERN_SITE == 0:
warning_array.append("warning_site_support_email_equal_to_default")
#Check for a new release of Invenio
try:
find = re.compile('Invenio v[0-9]+.[0-9]+.[0-9]+ is released')
webFile = urllib.urlopen("http://cdsware.cern.ch/download/RELEASE-NOTES")
temp = ""
version = ""
version1 = ""
while 1:
temp = webFile.readline()
match1 = find.match(temp)
try:
version = match1.group()
break
except:
pass
if not temp:
break
webFile.close()
submatch = re.compile('[0-9]+.[0-9]+.[0-9]+')
version1 = submatch.search(version)
web_version = version1.group().split(".")
local_version = CFG_VERSION.split(".")
if web_version[0] > local_version[0]:
warning_array.append("note_new_release_available")
elif web_version[0] == local_version[0] and web_version[1] > local_version[1]:
warning_array.append("note_new_release_available")
elif web_version[0] == local_version[0] and web_version[1] == local_version[1] and web_version[2] > local_version[2]:
warning_array.append("note_new_release_available")
except:
warning_array.append("error_cannot_download_release_notes")
return warning_array
def template_account(title, body, ln):
"""It is a template for print each of the options from the user's account."""
return websession_templates.tmpl_account_template(
ln = ln,
title = title,
body = body
)
def warning_guest_user(type, ln=CFG_SITE_LANG):
"""It returns an alert message,showing that the user is a guest user and should log into the system."""
# load the right message language
_ = gettext_set_language(ln)
return websession_templates.tmpl_warning_guest_user(
ln = ln,
type = type,
)
def perform_delete(ln):
"""Delete the account of the user, not implement yet."""
# TODO
return websession_templates.tmpl_account_delete(ln = ln)
def perform_set(email, ln, can_config_bibcatalog = False, verbose = 0):
"""Perform_set(email,password): edit your account parameters, email and
password.
If can_config_bibcatalog is True, show the bibcatalog dialog (if configured).
"""
try:
res = run_sql("SELECT id, nickname FROM user WHERE email=%s", (email,))
uid = res[0][0]
nickname = res[0][1]
except:
uid = 0
nickname = ""
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS
prefs = get_user_preferences(uid)
- if CFG_EXTERNAL_AUTHENTICATION.has_key(prefs['login_method']) and CFG_EXTERNAL_AUTHENTICATION[prefs['login_method']][0]:
+ if CFG_EXTERNAL_AUTHENTICATION.has_key(prefs['login_method']) and CFG_EXTERNAL_AUTHENTICATION[prefs['login_method']] is not None:
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL = 3
out = websession_templates.tmpl_user_preferences(
ln = ln,
email = email,
email_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL >= 2),
password_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL >= 3),
nickname = nickname,
)
if len(CFG_EXTERNAL_AUTHENTICATION) > 1:
try:
uid = run_sql("SELECT id FROM user where email=%s", (email,))
uid = uid[0][0]
except:
uid = 0
current_login_method = prefs['login_method']
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
# Filtering out methods that don't provide user_exists to check if
# a user exists in the external auth method before letting him/her
# to switch.
for method in methods:
- if CFG_EXTERNAL_AUTHENTICATION[method][0]:
+ if CFG_EXTERNAL_AUTHENTICATION[method] is not None:
try:
- if not CFG_EXTERNAL_AUTHENTICATION[method][0].user_exists(email):
+ if not CFG_EXTERNAL_AUTHENTICATION[method].user_exists(email):
methods.remove(method)
- except (AttributeError, InvenioWebAccessExternalAuthError):
+ except (AttributeError, InvenioWebAccessExternalAuthError, NotImplementedError):
methods.remove(method)
methods.sort()
if len(methods) > 1:
out += websession_templates.tmpl_user_external_auth(
ln = ln,
methods = methods,
current = current_login_method,
method_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4)
)
current_group_records = prefs.get('websearch_group_records', 10)
show_latestbox = prefs.get('websearch_latestbox', True)
show_helpbox = prefs.get('websearch_helpbox', True)
out += websession_templates.tmpl_user_websearch_edit(
ln = ln,
current = current_group_records,
show_latestbox = show_latestbox,
show_helpbox = show_helpbox,
)
preferred_lang = prefs.get('language', ln)
out += websession_templates.tmpl_user_lang_edit(
ln = ln,
preferred_lang = preferred_lang
)
#show this dialog only if the system has been configured to use a ticket system
from invenio.config import CFG_BIBCATALOG_SYSTEM
if CFG_BIBCATALOG_SYSTEM and can_config_bibcatalog:
bibcatalog_username = prefs.get('bibcatalog_username', "")
bibcatalog_password = prefs.get('bibcatalog_password', "")
out += websession_templates.tmpl_user_bibcatalog_auth(bibcatalog_username, \
bibcatalog_password, ln=ln)
if verbose >= 9:
for key, value in prefs.items():
out += "%s:%s " % (key, value)
out += perform_display_external_user_settings(prefs, ln)
return out
def create_register_page_box(referer='', ln=CFG_SITE_LANG):
"""Register a new account."""
return websession_templates.tmpl_register_page(
referer = referer,
ln = ln,
level = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS,
)
## create_login_page_box(): ask for the user's email and password, for login into the system
-def create_login_page_box(referer='', apache_msg="", ln=CFG_SITE_LANG):
+def create_login_page_box(referer='', ln=CFG_SITE_LANG):
# List of referer regexep and message to print
_ = gettext_set_language(ln)
login_referrer2msg = (
(re.compile(r"/search"), "
" + _("This collection is restricted. If you think you have right to access it, please authenticate yourself.") + "
"),
(re.compile(r"/record/\d+/files/.+"), "
" + _("This file is restricted. If you think you have right to access it, please authenticate yourself.") + "
"),
)
msg = ""
for regexp, txt in login_referrer2msg:
if regexp.search(referer):
msg = txt
break
- # FIXME: Temporary Hack to help CDS current migration
- if CFG_CERN_SITE and apache_msg:
- return msg + apache_msg
-
- if apache_msg:
- msg += apache_msg + "
2) Otherwise please authenticate yourself" \
- " in the following form:
"
-
internal = None
for system in CFG_EXTERNAL_AUTHENTICATION.keys():
- if not CFG_EXTERNAL_AUTHENTICATION[system][0]:
+ if CFG_EXTERNAL_AUTHENTICATION[system] is None:
internal = system
break
register_available = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS <= 1 and internal
- methods = CFG_EXTERNAL_AUTHENTICATION.keys()
+ ## 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()
- selected = ''
- for method in methods:
- if CFG_EXTERNAL_AUTHENTICATION[method][1]:
- selected = method
- break
return websession_templates.tmpl_login_form(
ln = ln,
referer = referer,
internal = internal,
register_available = register_available,
methods = methods,
- selected_method = selected,
+ selected_method = CFG_EXTERNAL_AUTH_DEFAULT,
msg = msg,
)
# perform_logout: display the message of not longer authorized,
def perform_logout(req, ln):
return websession_templates.tmpl_account_logout(ln = ln)
#def perform_lost: ask the user for his email, in order to send him the lost password
def perform_lost(ln):
return websession_templates.tmpl_lost_password_form(ln)
#def perform_reset_password: ask the user for a new password to reset the lost one
def perform_reset_password(ln, email, reset_key, msg=''):
return websession_templates.tmpl_reset_password_form(ln, email, reset_key, msg)
# perform_emailSent(email): confirm that the password has been emailed to 'email' address
def perform_emailSent(email, ln):
return websession_templates.tmpl_account_emailSent(ln = ln, email = email)
# peform_emailMessage : display a error message when the email introduced is not correct, and sugest to try again
def perform_emailMessage(eMsg, ln):
return websession_templates.tmpl_account_emailMessage( ln = ln,
msg = eMsg
)
# perform_back(): template for return to a previous page, used for login,register and setting
def perform_back(mess, url, linkname, ln='en'):
return websession_templates.tmpl_back_form(
ln = ln,
message = mess,
url = url,
link = linkname,
)
diff --git a/modules/websession/lib/webgroup.py b/modules/websession/lib/webgroup.py
index 707988d79..ca678c47b 100644
--- a/modules/websession/lib/webgroup.py
+++ b/modules/websession/lib/webgroup.py
@@ -1,883 +1,883 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Group features."""
__revision__ = "$Id$"
import sys
from invenio.config import CFG_SITE_LANG
from invenio.messages import gettext_set_language
from invenio.websession_config import CFG_WEBSESSION_INFO_MESSAGES, \
CFG_WEBSESSION_USERGROUP_STATUS, \
CFG_WEBSESSION_GROUP_JOIN_POLICY
from invenio.webuser import nickname_valid_p, get_user_info
from invenio.webmessage import perform_request_send
import invenio.webgroup_dblayer as db
from invenio.dbquery import IntegrityError
try:
import invenio.template
websession_templates = invenio.template.load('websession')
except ImportError:
pass
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
def perform_request_groups_display(uid, infos=[], errors = [], warnings = [], \
ln=CFG_SITE_LANG):
"""Display all the groups the user belongs to.
@param uid: user id
@param info: info about last user action
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
_ = gettext_set_language(ln)
body = ""
(body_admin, errors_admin) = display_admin_groups(uid, ln)
(body_member, errors_member) = display_member_groups(uid, ln)
(body_external, errors_external) = display_external_groups(uid, ln)
if errors_admin:
errors.extend(errors_admin)
if errors_member:
errors.extend(errors_member)
if errors_external:
errors.extend(errors_external)
body = websession_templates.tmpl_display_all_groups(infos=infos,
admin_group_html=body_admin,
member_group_html=body_member,
external_group_html=body_external,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def display_admin_groups(uid, ln=CFG_SITE_LANG):
"""Display groups the user is admin of.
@param uid: user id
@param ln: language
@return: a (body, errors[]) formed tuple
return html groups representation the user is admin of
"""
body = ""
errors = []
record = db.get_groups_by_user_status(uid=uid,
user_status=CFG_WEBSESSION_USERGROUP_STATUS["ADMIN"])
body = websession_templates.tmpl_display_admin_groups(groups=record,
ln=ln)
return (body, errors)
def display_member_groups(uid, ln=CFG_SITE_LANG):
"""Display groups the user is member of.
@param uid: user id
@param ln: language
@return: a (body, errors[]) formed tuple
body : html groups representation the user is member of
"""
body = ""
errors = []
records = db.get_groups_by_user_status(uid,
user_status=CFG_WEBSESSION_USERGROUP_STATUS["MEMBER"] )
body = websession_templates.tmpl_display_member_groups(groups=records,
ln=ln)
return (body, errors)
def display_external_groups(uid, ln=CFG_SITE_LANG):
"""Display groups the user is admin of.
@param uid: user id
@param ln: language
@return: a (body, errors[]) formed tuple
return html groups representation the user is admin of
"""
body = ""
errors = []
record = db.get_external_groups(uid)
if record:
body = websession_templates.tmpl_display_external_groups(groups=record,
ln=ln)
else:
body = None
return (body, errors)
def perform_request_input_create_group(group_name,
group_description,
join_policy,
warnings=[],
ln=CFG_SITE_LANG):
"""Display form for creating new group.
@param group_name: name of the group entered if the page has been reloaded
@param group_description: description entered if the page has been reloaded
@param join_policy: join policy chosen if the page has been reloaded
@param warnings: warnings
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
body: html for group creation page
"""
body = ""
errors = []
body = websession_templates.tmpl_display_input_group_info(group_name,
group_description,
join_policy,
act_type="create",
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_create_group(uid,
group_name,
group_description,
join_policy,
ln=CFG_SITE_LANG):
"""Create new group.
@param group_name: name of the group entered
@param group_description: description of the group entered
@param join_policy: join policy of the group entered
@param ln: language
@return: a (body, errors, warnings) formed tuple
warning != [] if group_name or join_policy are not valid
or if the name already exists in the database
body="1" if succeed in order to display info on the main page
"""
_ = gettext_set_language(ln)
body = ""
warnings = []
errors = []
infos = []
if group_name == "":
warnings.append(('WRN_WEBSESSION_NO_GROUP_NAME',))
(body, errors, warnings) = perform_request_input_create_group(
group_name,
group_description,
join_policy,
warnings=warnings)
elif not group_name_valid_p(group_name):
warnings.append('WRN_WEBSESSION_NOT_VALID_GROUP_NAME')
(body, errors, warnings) = perform_request_input_create_group(
group_name,
group_description,
join_policy,
warnings=warnings)
elif join_policy=="-1":
warnings.append('WRN_WEBSESSION_NO_JOIN_POLICY')
(body, errors, warnings) = perform_request_input_create_group(
group_name,
group_description,
join_policy,
warnings=warnings)
elif db.group_name_exist(group_name):
warnings.append('WRN_WEBSESSION_GROUP_NAME_EXISTS')
(body, errors, warnings) = perform_request_input_create_group(
group_name,
group_description,
join_policy,
warnings=warnings)
else:
db.insert_new_group(uid,
group_name,
group_description,
join_policy)
infos.append(CFG_WEBSESSION_INFO_MESSAGES["GROUP_CREATED"])
(body, errors, warnings) = perform_request_groups_display(uid,
infos=infos,
errors=errors,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_input_join_group(uid,
group_name,
search,
warnings=[],
ln=CFG_SITE_LANG):
"""Return html for joining new group.
@param group_name: name of the group entered if user is looking for a group
@param search=1 if search performed else 0
@param warnings: warnings coming from perform_request_join_group
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
errors = []
group_from_search = {}
records = db.get_visible_group_list(uid=uid)
if search:
group_from_search = db.get_visible_group_list(uid, group_name)
body = websession_templates.tmpl_display_input_join_group(records.items(),
group_name,
group_from_search.items(),
search,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_join_group(uid,
grpID,
group_name,
search,
ln=CFG_SITE_LANG):
"""Join group.
@param grpID: list of the groups the user wants to join,
only one value must be selected among the two group lists
(default group list, group list resulting from the search)
@param group_name: name of the group entered if search on name performed
@param search=1 if search performed else 0
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
warnings != [] if 0 or more than one group is selected
"""
_ = gettext_set_language(ln)
body = ""
warnings = []
errors = []
infos = []
if "-1" in grpID:
grpID = filter(lambda x: x != '-1', grpID)
if len(grpID)==1 :
grpID = int(grpID[0])
# test if user is already member or pending
status = db.get_user_status(uid, grpID)
if status:
warnings.append('WRN_WEBSESSION_ALREADY_MEMBER')
(body, errors, warnings) = perform_request_groups_display(uid,
infos=infos,
errors=errors,
warnings=warnings,
ln=ln)
# insert new user of group
else:
group_infos = db.get_group_infos(grpID)
group_type = group_infos[0][3]
if group_type == CFG_WEBSESSION_GROUP_JOIN_POLICY["VISIBLEMAIL"]:
db.insert_new_member(uid,
grpID,
CFG_WEBSESSION_USERGROUP_STATUS["PENDING"])
admin = db.get_users_by_status(grpID,
CFG_WEBSESSION_USERGROUP_STATUS["ADMIN"])[0][1]
group_name = group_infos[0][1]
msg_subjet, msg_body = websession_templates.tmpl_admin_msg(
group_name=group_name,
grpID=grpID,
ln=ln)
(body, errors, warnings, dummy, dummy) = \
perform_request_send(uid,
msg_to_user=admin,
msg_to_group="",
msg_subject=msg_subjet,
msg_body=msg_body,
ln=ln)
infos.append(CFG_WEBSESSION_INFO_MESSAGES["JOIN_REQUEST"])
elif group_type == CFG_WEBSESSION_GROUP_JOIN_POLICY["VISIBLEOPEN"]:
db.insert_new_member(uid,
grpID,
CFG_WEBSESSION_USERGROUP_STATUS["MEMBER"])
infos.append(CFG_WEBSESSION_INFO_MESSAGES["JOIN_GROUP"])
(body, errors, warnings) = perform_request_groups_display(uid,
infos=infos,
errors=errors,
warnings=warnings,
ln=ln)
else:
warnings.append('WRN_WEBSESSION_MULTIPLE_GROUPS')
(body, errors, warnings) = perform_request_input_join_group(uid,
group_name,
search,
warnings,
ln)
return (body, errors, warnings)
def perform_request_input_leave_group(uid,
warnings=[],
ln=CFG_SITE_LANG):
"""Return html for leaving group.
@param uid: user ID
@param warnings: warnings != [] if 0 group is selected or if not admin
of the
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ""
errors = []
groups = []
records = db.get_groups_by_user_status(uid=uid,
user_status=CFG_WEBSESSION_USERGROUP_STATUS["MEMBER"])
map(lambda x: groups.append((x[0], x[1])), records)
body = websession_templates.tmpl_display_input_leave_group(groups,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_leave_group(uid, grpID, confirmed=0, ln=CFG_SITE_LANG):
"""Leave group.
@param uid: user ID
@param grpID: ID of the group the user wants to leave
@param warnings: warnings != [] if 0 group is selected
@param confirmed: a confirmed page is first displayed
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
_ = gettext_set_language(ln)
body = ""
warnings = []
errors = []
infos = []
if not grpID == -1:
if confirmed:
db.leave_group(grpID, uid)
infos.append(CFG_WEBSESSION_INFO_MESSAGES["LEAVE_GROUP"])
(body, errors, warnings) = perform_request_groups_display(uid,
infos=infos, errors=errors, warnings=warnings, ln=ln)
else:
body = websession_templates.tmpl_confirm_leave(uid, grpID, ln)
else:
warnings.append('WRN_WEBSESSION_NO_GROUP_SELECTED')
(body, errors, warnings) = perform_request_input_leave_group(uid,
warnings= warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_edit_group(uid,
grpID,
warnings=[],
ln=CFG_SITE_LANG):
"""Return html for group editing.
@param uid: user ID
@param grpID: ID of the group
@param warnings: warnings
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ''
errors = []
user_status = db.get_user_status(uid, grpID)
if not len(user_status):
errors.append('ERR_WEBSESSION_DB_ERROR')
return (body, errors, warnings)
elif user_status[0][0] != CFG_WEBSESSION_USERGROUP_STATUS['ADMIN']:
errors.append(('ERR_WEBSESSION_GROUP_NO_RIGHTS',))
return (body, errors, warnings)
group_infos = db.get_group_infos(grpID)[0]
if not len(group_infos):
errors.append('ERR_WEBSESSION_DB_ERROR')
return (body, errors, warnings)
body = websession_templates.tmpl_display_input_group_info(
group_name=group_infos[1],
group_description=group_infos[2],
join_policy=group_infos[3],
act_type="update",
grpID=grpID,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_update_group(uid, grpID, group_name, group_description,
join_policy, ln=CFG_SITE_LANG):
"""Update group datas in database.
@param uid: user ID
@param grpID: ID of the group
@param group_name: name of the group
@param group_description: description of the group
@param join_policy: join policy of the group
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ''
errors = []
warnings = []
infos = []
_ = gettext_set_language(ln)
group_name_available = db.group_name_exist(group_name)
if group_name == "":
warnings.append('WRN_WEBSESSION_NO_GROUP_NAME')
(body, errors, warnings) = perform_request_edit_group(uid,
grpID,
warnings=warnings,
ln=ln)
elif not group_name_valid_p(group_name):
warnings.append('WRN_WEBSESSION_NOT_VALID_GROUP_NAME')
(body, errors, warnings) = perform_request_edit_group(uid,
grpID,
warnings=warnings,
ln=ln)
elif join_policy == "-1":
warnings.append('WRN_WEBSESSION_NO_JOIN_POLICY')
(body, errors, warnings) = perform_request_edit_group(uid,
grpID,
warnings=warnings,
ln=ln)
elif (group_name_available and group_name_available[0][0]!= grpID):
warnings.append('WRN_WEBSESSION_GROUP_NAME_EXISTS')
(body, errors, warnings) = perform_request_edit_group(uid,
grpID,
warnings=warnings,
ln=ln)
else:
grpID = db.update_group_infos(grpID,
group_name,
group_description,
join_policy)
infos.append(CFG_WEBSESSION_INFO_MESSAGES["GROUP_UPDATED"])
(body, errors, warnings) = perform_request_groups_display(uid,
infos=infos,
errors=errors,
warnings=warnings,
ln=CFG_SITE_LANG)
return (body, errors, warnings)
def perform_request_delete_group(uid, grpID, confirmed=0, ln=CFG_SITE_LANG):
"""First display confirm message(confirmed=0).
then(confirmed=1) delete group and all its members
@param uid: user ID
@param grpID: ID of the group
@param confirmed: =1 if confirmed message has been previously displayed
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ""
warnings = []
errors = []
infos = []
_ = gettext_set_language(ln)
group_infos = db.get_group_infos(grpID)
user_status = db.get_user_status(uid, grpID)
if not group_infos:
warnings.append('WRN_WEBSESSION_GROUP_ALREADY_DELETED')
(body, errors, warnings) = perform_request_groups_display(uid,
infos=infos,
errors=errors,
warnings=warnings,
ln=CFG_SITE_LANG)
else:
if not len(user_status):
errors.append('ERR_WEBSESSION_DB_ERROR')
elif confirmed:
group_infos = db.get_group_infos(grpID)
group_name = group_infos[0][1]
msg_subjet, msg_body = websession_templates.tmpl_delete_msg(
group_name=group_name, ln=ln)
(body, errors, warnings, dummy, dummy) = perform_request_send(
uid,
msg_to_user="",
msg_to_group=group_name,
msg_subject=msg_subjet,
msg_body=msg_body,
ln=ln)
db.delete_group_and_members(grpID)
infos.append(CFG_WEBSESSION_INFO_MESSAGES["GROUP_DELETED"])
(body, errors, warnings) = perform_request_groups_display(uid,
infos=infos,
errors=errors,
warnings=warnings,
ln=CFG_SITE_LANG)
else:
body = websession_templates.tmpl_confirm_delete(grpID, ln)
return (body, errors, warnings)
def perform_request_manage_member(uid,
grpID,
infos=[],
warnings=[],
ln=CFG_SITE_LANG):
"""Return html for managing group's members.
@param uid: user ID
@param grpID: ID of the group
@param info: info about last user action
@param warnings: warnings
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ''
errors = []
_ = gettext_set_language(ln)
user_status = db.get_user_status(uid, grpID)
if not len(user_status):
errors.append('ERR_WEBSESSION_DB_ERROR')
return (body, errors, warnings)
elif user_status[0][0] != CFG_WEBSESSION_USERGROUP_STATUS['ADMIN']:
errors.append(('ERR_WEBSESSION_GROUP_NO_RIGHTS',))
return (body, errors, warnings)
group_infos = db.get_group_infos(grpID)
if not len(group_infos):
errors.append('ERR_WEBSESSION_DB_ERROR')
return (body, errors, warnings)
members = db.get_users_by_status(grpID,
CFG_WEBSESSION_USERGROUP_STATUS["MEMBER"])
pending_members = db.get_users_by_status(grpID,
CFG_WEBSESSION_USERGROUP_STATUS["PENDING"])
body = websession_templates.tmpl_display_manage_member(grpID=grpID,
group_name=group_infos[0][1],
members=members,
pending_members=pending_members,
warnings=warnings,
infos=infos,
ln=ln)
return (body, errors, warnings)
def perform_request_remove_member(uid, grpID, member_id, ln=CFG_SITE_LANG):
"""Remove member from a group.
@param uid: user ID
@param grpID: ID of the group
@param member_id: selected member ID
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ''
errors = []
warnings = []
infos = []
_ = gettext_set_language(ln)
user_status = db.get_user_status(uid, grpID)
if not len(user_status):
errors.append('ERR_WEBSESSION_DB_ERROR')
return (body, errors, warnings)
if member_id == -1:
warnings.append('WRN_WEBSESSION_NO_MEMBER_SELECTED')
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
warnings=warnings,
ln=ln)
else:
db.delete_member(grpID, member_id)
infos.append(CFG_WEBSESSION_INFO_MESSAGES["MEMBER_DELETED"])
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
infos=infos,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_add_member(uid, grpID, user_id, ln=CFG_SITE_LANG):
"""Add waiting member to a group.
@param uid: user ID
@param grpID: ID of the group
@param user_id: selected member ID
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ''
errors = []
warnings = []
infos = []
_ = gettext_set_language(ln)
user_status = db.get_user_status(uid, grpID)
if not len(user_status):
errors.append('ERR_WEBSESSION_DB_ERROR')
return (body, errors, warnings)
if user_id == -1:
warnings.append('WRN_WEBSESSION_NO_USER_SELECTED_ADD')
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
warnings=warnings,
ln=ln)
else :
# test if user is already member or pending
status = db.get_user_status(user_id, grpID)
if status and status[0][0] == 'M':
warnings.append('WRN_WEBSESSION_ALREADY_MEMBER_ADD')
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
infos=infos,
warnings=warnings,
ln=ln)
else:
db.add_pending_member(grpID,
user_id,
CFG_WEBSESSION_USERGROUP_STATUS["MEMBER"])
infos.append(CFG_WEBSESSION_INFO_MESSAGES["MEMBER_ADDED"])
group_infos = db.get_group_infos(grpID)
group_name = group_infos[0][1]
user = get_user_info(user_id, ln)[2]
msg_subjet, msg_body = websession_templates.tmpl_member_msg(
group_name=group_name, accepted=1, ln=ln)
(body, errors, warnings, dummy, dummy) = perform_request_send(
uid, msg_to_user=user, msg_to_group="", msg_subject=msg_subjet,
msg_body=msg_body, ln=ln)
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
infos=infos,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def perform_request_reject_member(uid,
grpID,
user_id,
ln=CFG_SITE_LANG):
"""Reject waiting member and delete it from the list.
@param uid: user ID
@param grpID: ID of the group
@param member_id: selected member ID
@param ln: language
@return: a (body, errors[], warnings[]) formed tuple
"""
body = ''
errors = []
warnings = []
infos = []
_ = gettext_set_language(ln)
user_status = db.get_user_status(uid, grpID)
if not len(user_status):
errors.append('ERR_WEBSESSION_DB_ERROR')
return (body, errors, warnings)
if user_id == -1:
warnings.append('WRN_WEBSESSION_NO_USER_SELECTED_DEL')
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
warnings=warnings,
ln=ln)
else :
# test if user is already member or pending
status = db.get_user_status(user_id, grpID)
if not status:
warnings.append('WRN_WEBSESSION_ALREADY_MEMBER_REJECT')
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
infos=infos,
warnings=warnings,
ln=ln)
else:
db.delete_member(grpID, user_id)
group_infos = db.get_group_infos(grpID)
group_name = group_infos[0][1]
user = get_user_info(user_id, ln)[2]
msg_subjet, msg_body = websession_templates.tmpl_member_msg(
group_name=group_name,
accepted=0,
ln=ln)
(body, errors, warnings, dummy, dummy) = perform_request_send(
uid,
msg_to_user=user,
msg_to_group="",
msg_subject=msg_subjet,
msg_body=msg_body,
ln=ln)
infos.append(CFG_WEBSESSION_INFO_MESSAGES["MEMBER_REJECTED"])
(body, errors, warnings) = perform_request_manage_member(uid,
grpID,
infos=infos,
warnings=warnings,
ln=ln)
return (body, errors, warnings)
def account_group(uid, ln=CFG_SITE_LANG):
"""Display group info for myaccount.py page.
@param uid: user id (int)
@param ln: language
@return: html body
"""
nb_admin_groups = db.count_nb_group_user(uid,
CFG_WEBSESSION_USERGROUP_STATUS["ADMIN"])
nb_member_groups = db.count_nb_group_user(uid,
CFG_WEBSESSION_USERGROUP_STATUS["MEMBER"])
nb_total_groups = nb_admin_groups + nb_member_groups
return websession_templates.tmpl_group_info(nb_admin_groups,
nb_member_groups,
nb_total_groups,
ln=ln)
def get_navtrail(ln=CFG_SITE_LANG, title=""):
"""Gets the navtrail for title.
@param title: title of the page
@param ln: language
@return: HTML output
"""
navtrail = websession_templates.tmpl_navtrail(ln, title)
return navtrail
def synchronize_external_groups(userid, groups, login_method):
"""Synchronize external groups adding new groups that aren't already
added, adding subscription for userid to groups, for groups that the user
isn't already subsribed to, removing subscription to groups the user is no
more subsribed to.
@param userid, the intger representing the user inside the db
@param groups, a dictionary of group_name : group_description
@param login_method, a string unique to the type of authentication
the groups are associated to, to be used inside the db
"""
groups_already_known = db.get_login_method_groups(userid, login_method)
group_dict = {}
for name, groupid in groups_already_known:
group_dict[name] = groupid
groups_already_known_name = set([g[0] for g in groups_already_known])
groups_name = set(groups.keys())
nomore_groups = groups_already_known_name - groups_name
for group in nomore_groups: # delete the user from no more affiliated group
db.delete_member(group_dict[group], userid)
potential_new_groups = groups_name - groups_already_known_name
for group in potential_new_groups:
groupid = db.get_group_id(group, login_method)
if groupid: # Adding the user to an already existent group
db.insert_new_member(userid, groupid[0][0],
CFG_WEBSESSION_USERGROUP_STATUS['MEMBER'])
else: # Adding a new group
try:
groupid = db.insert_new_group(userid, group, groups[group], \
CFG_WEBSESSION_GROUP_JOIN_POLICY['VISIBLEEXTERNAL'], \
login_method)
db.add_pending_member(groupid, userid,
CFG_WEBSESSION_USERGROUP_STATUS['MEMBER'])
except IntegrityError:
## The group already exists? Maybe because of concurrency?
groupid = db.get_group_id(group, login_method)
if groupid: # Adding the user to an already existent group
db.insert_new_member(userid, groupid[0][0],
CFG_WEBSESSION_USERGROUP_STATUS['MEMBER'])
def synchronize_groups_with_login_method():
"""For each login_method, if possible, synchronize groups in a bulk fashion
(i.e. when fetch_all_users_groups_membership is implemented in the
external_authentication class). Otherwise, for each user that belong to at
least one external group for a given login_method, ask, if possible, for
his group memberships and merge them.
"""
from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION
for login_method, authorizer in CFG_EXTERNAL_AUTHENTICATION.items():
- if authorizer[0]:
+ if authorizer:
try:
- usersgroups = authorizer[0].fetch_all_users_groups_membership()
+ usersgroups = authorizer.fetch_all_users_groups_membership()
synchronize_all_external_groups(usersgroups, login_method)
except (NotImplementedError, NameError):
users = db.get_all_users_with_groups_with_login_method(
login_method)
for email, uid in users.items():
try:
- groups = authorizer[0].fetch_user_groups_membership(
+ groups = authorizer.fetch_user_groups_membership(
email)
synchronize_external_groups(uid, groups, login_method)
except (NotImplementedError, NameError):
pass
def synchronize_all_external_groups(usersgroups, login_method):
"""Merges all the groups vs users memberships.
@param usersgroups: is {'mygroup': ('description',
['email1', 'email2', ...]), ...}
@return: True in case everythings is ok, False otherwise
"""
db_users = db.get_all_users() # All users of the database {email:uid, ...}
db_users_set = set(db_users.keys()) # Set of all users set('email1',
# 'email2', ...)
for key, value in usersgroups.items():
# cleaning users not in db
cleaned_user_list = set()
for username in value[1]:
username = username.upper()
if username in db_users_set:
cleaned_user_list.add(db_users[username])
if cleaned_user_list:
usersgroups[key] = (value[0], cleaned_user_list)
else: # List of user now is empty
del usersgroups[key] # cleaning not interesting groups
# now for each group we got a description and a set of uid
groups_already_known = db.get_all_login_method_groups(login_method)
# groups in the db {groupname: id}
groups_already_known_set = set(groups_already_known.keys())
# set of the groupnames in db
usersgroups_set = set(usersgroups.keys()) # set of groupnames to be merged
# deleted groups!
nomore_groups = groups_already_known_set - usersgroups_set
for group_name in nomore_groups:
db.delete_group_and_members(groups_already_known[group_name])
# new groups!
new_groups = usersgroups_set - groups_already_known_set
for group_name in new_groups:
groupid = db.insert_only_new_group(
group_name,
usersgroups[group_name][0], # description
CFG_WEBSESSION_GROUP_JOIN_POLICY['VISIBLEEXTERNAL'],
login_method)
for uid in usersgroups[group_name][1]:
db.insert_new_member(uid,
groupid,
CFG_WEBSESSION_USERGROUP_STATUS['MEMBER'])
# changed groups?
changed_groups = usersgroups_set & groups_already_known_set
groups_description = db.get_all_groups_description(login_method)
for group_name in changed_groups:
users_already_in_group = db.get_users_in_group(
groups_already_known[group_name])
users_already_in_group_set = set(users_already_in_group)
users_in_group_set = usersgroups[group_name][1]
# no more affiliation
nomore_users = users_already_in_group_set - users_in_group_set
for uid in nomore_users:
db.delete_member(groups_already_known[group_name], uid)
# new affiliation
new_users = users_in_group_set - users_already_in_group_set
for uid in new_users:
db.insert_new_member(uid,
groups_already_known[group_name],
CFG_WEBSESSION_USERGROUP_STATUS['MEMBER'])
# check description
if groups_description[group_name] != usersgroups[group_name][0]:
db.update_group_infos(groups_already_known[group_name],
group_name,
usersgroups[group_name][0],
CFG_WEBSESSION_GROUP_JOIN_POLICY['VISIBLEEXTERNAL'])
def group_name_valid_p(group_name):
"""Test if the group's name is valid."""
return nickname_valid_p(group_name)
diff --git a/modules/websession/lib/websession_webinterface.py b/modules/websession/lib/websession_webinterface.py
index 56745c776..799474296 100644
--- a/modules/websession/lib/websession_webinterface.py
+++ b/modules/websession/lib/websession_webinterface.py
@@ -1,1296 +1,1357 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio ACCOUNT HANDLING"""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import cgi
from datetime import timedelta
+import os
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_SITE_URL, \
CFG_CERN_SITE, \
CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS
from invenio import webuser
from invenio.webpage import page
from invenio import webaccount
from invenio import webbasket
from invenio import webalert
from invenio.dbquery import run_sql
from invenio.webmessage import account_new_mail
-from invenio.access_control_engine import make_apache_message, make_list_apache_firerole, acc_authorize_action
+from invenio.access_control_engine import acc_authorize_action
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
+from invenio.webinterface_handler_config import HTTP_BAD_REQUEST
from invenio.urlutils import redirect_to_url, make_canonical_urlargd
from invenio import webgroup
from invenio import webgroup_dblayer
from invenio.messages import gettext_set_language, wash_language
from invenio.mailutils import send_email
from invenio.access_control_mailcookie import mail_cookie_retrieve_kind, \
mail_cookie_check_pw_reset, mail_cookie_delete_cookie, \
mail_cookie_create_pw_reset, mail_cookie_check_role, \
mail_cookie_check_mail_activation, InvenioWebAccessMailCookieError, \
InvenioWebAccessMailCookieDeletedError, mail_cookie_check_authorize_action
from invenio.access_control_config import CFG_WEBACCESS_WARNING_MSGS, \
CFG_EXTERNAL_AUTH_USING_SSO, CFG_EXTERNAL_AUTH_LOGOUT_SSO, \
CFG_EXTERNAL_AUTHENTICATION
import invenio.template
websession_templates = invenio.template.load('websession')
bibcatalog_templates = invenio.template.load('bibcatalog')
class WebInterfaceYourAccountPages(WebInterfaceDirectory):
_exports = ['', 'edit', 'change', 'lost', 'display',
'send_email', 'youradminactivities', 'access',
- 'delete', 'logout', 'login', 'register', 'resetpassword']
+ 'delete', 'logout', 'login', 'register', 'resetpassword',
+ 'robotlogin', 'robotlogout']
_force_https = True
def index(self, req, form):
redirect_to_url(req, '%s/youraccount/display' % CFG_SITE_SECURE_URL)
def access(self, req, form):
args = wash_urlargd(form, {'mailcookie' : (str, '')})
_ = gettext_set_language(args['ln'])
title = _("Mail Cookie Service")
try:
kind = mail_cookie_retrieve_kind(args['mailcookie'])
if kind == 'pw_reset':
redirect_to_url(req, '%s/youraccount/resetpassword?k=%s&ln=%s' % (CFG_SITE_SECURE_URL, args['mailcookie'], args['ln']))
elif kind == 'role':
uid = webuser.getUid(req)
try:
(role_name, expiration) = mail_cookie_check_role(args['mailcookie'], uid)
except InvenioWebAccessMailCookieDeletedError:
return page(title=_("Role authorization request"), req=req, body=_("This request for an authorization has already been authorized."), uid=webuser.getUid(req), navmenuid='youraccount', language=args['ln'])
return page(title=title,
body=webaccount.perform_back(
_("You have successfully obtained an authorization as %(x_role)s! "
"This authorization will last until %(x_expiration)s and until "
"you close your browser if you are a guest user.") %
{'x_role' : '%s' % role_name,
'x_expiration' : '%s' % expiration.strftime("%Y-%m-%d %H:%M:%S")},
'/youraccount/display?ln=%s' % args['ln'], _('login'), args['ln']),
req=req,
uid=webuser.getUid(req),
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
elif kind == 'mail_activation':
try:
email = mail_cookie_check_mail_activation(args['mailcookie'])
if not email:
raise StandardError
webuser.confirm_email(email)
body = "
" + _("You have confirmed the validity of your email"
" address!") + "
"
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
body += "
" + _("Please, wait for the administrator to "
"enable your account.") + "
"
else:
uid = webuser.update_Uid(req, email)
body += "
" + _("You can now go to %(x_url_open)syour account page%(x_url_close)s.") % {'x_url_open' : '' % args['ln'], 'x_url_close' : ''} + "
" + _("You have already confirmed the validity of your email address!") + "
"
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
body += "
" + _("Please, wait for the administrator to "
"enable your account.") + "
"
else:
body += "
" + _("You can now go to %(x_url_open)syour account page%(x_url_close)s.") % {'x_url_open' : '' % args['ln'], 'x_url_close' : ''} + "
"
return page(title=_("Email address successfully activated"),
body=body, req=req, language=args['ln'], uid=webuser.getUid(req), lastupdated=__lastupdated__, navmenuid='youraccount')
return webuser.page_not_authorized(req, "../youraccount/access",
text=_("This request for confirmation of an email "
"address is not valid or"
" is expired."), navmenuid='youraccount')
except InvenioWebAccessMailCookieError:
return webuser.page_not_authorized(req, "../youraccount/access",
text=_("This request for an authorization is not valid or"
" is expired."), navmenuid='youraccount')
def resetpassword(self, req, form):
args = wash_urlargd(form, {
'k' : (str, ''),
'reset' : (int, 0),
'password' : (str, ''),
'password2' : (str, '')
})
_ = gettext_set_language(args['ln'])
title = _('Reset password')
reset_key = args['k']
try:
email = mail_cookie_check_pw_reset(reset_key)
except InvenioWebAccessMailCookieDeletedError:
return page(title=title, req=req, body=_("This request for resetting a password has already been used."), uid=webuser.getUid(req), navmenuid='youraccount', language=args['ln'])
except InvenioWebAccessMailCookieError:
return webuser.page_not_authorized(req, "../youraccount/access",
text=_("This request for resetting a password is not valid or"
" is expired."), navmenuid='youraccount')
if email is None or CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 3:
return webuser.page_not_authorized(req, "../youraccount/resetpassword",
text=_("This request for resetting the password is not valid or"
" is expired."), navmenuid='youraccount')
if not args['reset']:
return page(title=title,
body=webaccount.perform_reset_password(args['ln'], email, reset_key),
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
elif args['password'] != args['password2']:
msg = _('The two provided passwords aren\'t equal.')
return page(title=title,
body=webaccount.perform_reset_password(args['ln'], email, reset_key, msg),
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
run_sql('UPDATE user SET password=AES_ENCRYPT(email,%s) WHERE email=%s', (args['password'], email))
mail_cookie_delete_cookie(reset_key)
return page(title=title,
body=webaccount.perform_back(
_("The password was successfully set! "
"You can now proceed with the login."),
'/youraccount/login?ln=%s' % args['ln'], _('login'), args['ln']),
req=req,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def display(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/display",
navmenuid='youraccount')
if webuser.isGuestUser(uid):
return page(title=_("Your Account"),
body=webaccount.perform_info(req, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
username = webuser.get_nickname_or_email(uid)
user_info = webuser.collect_user_info(req)
bask = user_info['precached_usebaskets'] and webbasket.account_list_baskets(uid, ln=args['ln']) or ''
aler = user_info['precached_usealerts'] and webalert.account_list_alerts(uid, ln=args['ln']) or ''
sear = webalert.account_list_searches(uid, ln=args['ln'])
msgs = user_info['precached_usemessages'] and account_new_mail(uid, ln=args['ln']) or ''
grps = user_info['precached_usegroups'] and webgroup.account_group(uid, ln=args['ln']) or ''
appr = user_info['precached_useapprove']
sbms = user_info['precached_viewsubmissions']
loan = ''
admn = webaccount.perform_youradminactivities(user_info, args['ln'])
return page(title=_("Your Account"),
body=webaccount.perform_display_account(req, username, bask, aler, sear, msgs, loan, grps, sbms, appr, admn, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def edit(self, req, form):
args = wash_urlargd(form, {"verbose" : (int, 0)})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/edit",
navmenuid='youraccount')
if webuser.isGuestUser(uid):
return webuser.page_not_authorized(req, "../youraccount/edit",
text=_("This functionality is forbidden to guest users."),
navmenuid='youraccount')
body = ''
user_info = webuser.collect_user_info(req)
if args['verbose'] == 9:
keys = user_info.keys()
keys.sort()
for key in keys:
body += "%s:%s " % (key, user_info[key])
#check if the user should see bibcatalog user name / passwd in the settings
can_config_bibcatalog = (acc_authorize_action(user_info, 'runbibedit')[0] == 0)
return page(title= _("Your Settings"),
body=body+webaccount.perform_set(webuser.get_email(uid),
args['ln'], can_config_bibcatalog,
verbose=args['verbose']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description=_("%s Personalize, Your Settings") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def change(self, req, form):
args = wash_urlargd(form, {
'nickname': (str, None),
'email': (str, None),
'old_password': (str, None),
'password': (str, None),
'password2': (str, None),
'login_method': (str, ""),
'group_records' : (int, None),
'latestbox' : (int, None),
'helpbox' : (int, None),
'lang' : (str, None),
'bibcatalog_username' : (str, None),
'bibcatalog_password' : (str, None),
})
## Wash arguments:
args['login_method'] = wash_login_method(args['login_method'])
if args['email']:
args['email'] = args['email'].lower()
## Load the right message language:
_ = gettext_set_language(args['ln'])
## Identify user and load old preferences:
uid = webuser.getUid(req)
prefs = webuser.get_user_preferences(uid)
## Check rights:
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/change",
navmenuid='youraccount')
# FIXME: the branching below is far from optimal. Should be
# based on the submitted form name ids, to know precisely on
# which form the user clicked. Not on the passed values, as
# is the case now. The function body is too big and in bad
# need of refactoring anyway.
## Will hold the output messages:
mess = ''
## Change login method if needed:
if args['login_method'] and CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 4 \
- and args['login_method'] in CFG_EXTERNAL_AUTHENTICATION.keys():
+ and args['login_method'] in CFG_EXTERNAL_AUTHENTICATION:
title = _("Settings edited")
act = "/youraccount/display?ln=%s" % args['ln']
linkname = _("Show account")
if prefs['login_method'] != args['login_method']:
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4:
mess += '
' + _("Unable to change login method.")
- elif not CFG_EXTERNAL_AUTHENTICATION[args['login_method']][0]:
+ elif not CFG_EXTERNAL_AUTHENTICATION[args['login_method']]:
# Switching to internal authentication: we drop any external datas
p_email = webuser.get_email(uid)
webuser.drop_external_settings(uid)
webgroup_dblayer.drop_external_groups(uid)
prefs['login_method'] = args['login_method']
webuser.set_user_preferences(uid, prefs)
mess += "
" + _("Switched to internal login method.") + " "
mess += _("Please note that if this is the first time that you are using this account "
"with the internal login method then the system has set for you "
"a randomly generated password. Please click the "
"following button to obtain a password reset request "
"link sent to you via email:") + '
'
mess += """""" % (p_email, _("Send Password"))
else:
query = """SELECT email FROM user
WHERE id = %i"""
res = run_sql(query % uid)
if res:
email = res[0][0]
else:
email = None
if not email:
mess += '
' + _("Unable to switch to external login method %s, because your email address is unknown.") % cgi.escape(args['login_method'])
else:
try:
- if not CFG_EXTERNAL_AUTHENTICATION[args['login_method']][0].user_exists(email):
+ if not CFG_EXTERNAL_AUTHENTICATION[args['login_method']].user_exists(email):
mess += '
' + _("Unable to switch to external login method %s, because your email address is unknown to the external login system.") % cgi.escape(args['login_method'])
else:
prefs['login_method'] = args['login_method']
webuser.set_user_preferences(uid, prefs)
mess += '
' + _("The external login method %s does not support email address based logins. Please contact the site administrators.") % cgi.escape(args['login_method'])
## Change email or nickname:
if args['email'] or args['nickname']:
uid2 = webuser.emailUnique(args['email'])
uid_with_the_same_nickname = webuser.nicknameUnique(args['nickname'])
if (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2 or (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS <= 1 and \
webuser.email_valid_p(args['email']))) \
and (args['nickname'] is None or webuser.nickname_valid_p(args['nickname'])) \
and uid2 != -1 and (uid2 == uid or uid2 == 0) \
and uid_with_the_same_nickname != -1 and (uid_with_the_same_nickname == uid or uid_with_the_same_nickname == 0):
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 3:
change = webuser.updateDataUser(uid,
args['email'],
args['nickname'])
else:
return webuser.page_not_authorized(req, "../youraccount/change",
navmenuid='youraccount')
if change:
mess += '
' + _("Settings successfully edited.")
mess += '
' + _("Note that if you have changed your email address, "
"you will have to %(x_url_open)sreset your password%(x_url_close)s anew.") % \
{'x_url_open': '' % (CFG_SITE_SECURE_URL + '/youraccount/lost?ln=%s' % args['ln']),
'x_url_close': ''}
act = "/youraccount/display?ln=%s" % args['ln']
linkname = _("Show account")
title = _("Settings edited")
elif args['nickname'] is not None and not webuser.nickname_valid_p(args['nickname']):
mess += '
' + _("Supplied email address %s is invalid.") % cgi.escape(args['email'])
mess += " " + _("Please try again.")
act = "/youraccount/edit?ln=%s" % args['ln']
linkname = _("Edit settings")
title = _("Editing settings failed")
elif uid2 == -1 or uid2 != uid and not uid2 == 0:
mess += '
' + _("Supplied email address %s already exists in the database.") % cgi.escape(args['email'])
mess += " " + websession_templates.tmpl_lost_your_password_teaser(args['ln'])
mess += " " + _("Or please try again.")
act = "/youraccount/edit?ln=%s" % args['ln']
linkname = _("Edit settings")
title = _("Editing settings failed")
elif uid_with_the_same_nickname == -1 or uid_with_the_same_nickname != uid and not uid_with_the_same_nickname == 0:
mess += '
' + _("Desired nickname %s is already in use.") % cgi.escape(args['nickname'])
mess += " " + _("Please try again.")
act = "/youraccount/edit?ln=%s" % args['ln']
linkname = _("Edit settings")
title = _("Editing settings failed")
## Change passwords:
if args['old_password'] or args['password'] or args['password2']:
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 3:
mess += '
' + _("Users cannot edit passwords on this site.")
else:
res = run_sql("SELECT id FROM user "
"WHERE AES_ENCRYPT(email,%s)=password AND id=%s",
(args['old_password'], uid))
if res:
if args['password'] == args['password2']:
webuser.updatePasswordUser(uid, args['password'])
mess += '
' + _("User settings saved correctly.")
if not mess:
mess = _("Unable to update settings.")
if not act:
act = "/youraccount/edit?ln=%s" % args['ln']
if not linkname:
linkname = _("Edit settings")
if not title:
title = _("Editing settings failed")
## Finally, output the results:
return page(title=title,
body=webaccount.perform_back(mess, act, linkname, args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def lost(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/lost",
navmenuid='youraccount')
return page(title=_("Lost your password?"),
body=webaccount.perform_lost(args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def send_email(self, req, form):
# set all the declared query fields as local variables
args = wash_urlargd(form, {'p_email': (str, None)})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/send_email",
navmenuid='youraccount')
user_prefs = webuser.get_user_preferences(webuser.emailUnique(args['p_email']))
if user_prefs:
- if CFG_EXTERNAL_AUTHENTICATION.has_key(user_prefs['login_method']) and \
- CFG_EXTERNAL_AUTHENTICATION[user_prefs['login_method']][0] is not None:
+ if user_prefs['login_method'] in CFG_EXTERNAL_AUTHENTICATION and \
+ CFG_EXTERNAL_AUTHENTICATION[user_prefs['login_method']] is not None:
eMsg = _("Cannot send password reset request since you are using external authentication system.")
return page(title=_("Your Account"),
body=webaccount.perform_emailMessage(eMsg, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME)),
uid=uid, req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
try:
reset_key = mail_cookie_create_pw_reset(args['p_email'], cookie_timeout=timedelta(days=CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS))
except InvenioWebAccessMailCookieError:
reset_key = None
if reset_key is None:
eMsg = _("The entered email address does not exist in the database.")
return page(title=_("Your Account"),
body=webaccount.perform_emailMessage(eMsg, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid, req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
ip_address = req.remote_host or req.remote_ip
if not send_email(CFG_SITE_SUPPORT_EMAIL, args['p_email'], "%s %s"
% (_("Password reset request for"),
CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME)),
websession_templates.tmpl_account_reset_password_email_body(
args['p_email'],reset_key, ip_address, args['ln'])):
eMsg = _("The entered email address is incorrect, please check that it is written correctly (e.g. johndoe@example.com).")
return page(title=_("Incorrect email address"),
body=webaccount.perform_emailMessage(eMsg, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
return page(title=_("Reset password link sent"),
body=webaccount.perform_emailSent(args['p_email'], args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid, req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def youradminactivities(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
user_info = webuser.collect_user_info(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/youradminactivities",
navmenuid='admin')
return page(title=_("Your Administrative Activities"),
body=webaccount.perform_youradminactivities(user_info, args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='admin')
def delete(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/delete",
navmenuid='youraccount')
return page(title=_("Delete Account"),
body=webaccount.perform_delete(args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def logout(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.logoutUser(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/logout",
navmenuid='youraccount')
if CFG_EXTERNAL_AUTH_USING_SSO:
return redirect_to_url(req, CFG_EXTERNAL_AUTH_LOGOUT_SSO)
return page(title=_("Logout"),
body=webaccount.perform_logout(req, args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
+ def robotlogout(self, req, form):
+ """
+ Implement logout method for external service providers.
+ """
+ webuser.logoutUser(req)
+ redirect_to_url(req, "%s/img/pix.png" % CFG_SITE_URL)
+
+ def robotlogin(self, req, form):
+ """
+ Implement authentication method for external service providers.
+ """
+ from invenio.external_authentication import InvenioWebAccessExternalAuthError
+ args = wash_urlargd(form, {
+ 'login_method': (str, None),
+ 'remember_me' : (str, ''),
+ 'referer': (str, ''),
+ 'p_un': (str, ''),
+ 'p_pw': (str, '')
+ })
+ # sanity checks:
+ args['login_method'] = wash_login_method(args['login_method'])
+ args['remember_me'] = args['remember_me'] != ''
+ locals().update(args)
+ if CFG_ACCESS_CONTROL_LEVEL_SITE > 0:
+ return webuser.page_not_authorized(req, "../youraccount/login?ln=%s" % args['ln'],
+ navmenuid='youraccount')
+ uid = webuser.getUid(req)
+
+ # load the right message language
+ _ = gettext_set_language(args['ln'])
+
+ try:
+ (iden, args['p_un'], args['p_pw'], msgcode) = webuser.loginUser(req, args['p_un'], args['p_pw'], args['login_method'])
+ except InvenioWebAccessExternalAuthError, err:
+ return page("Error", body=str(err))
+ if len(iden)>0:
+ uid = webuser.update_Uid(req, args['p_un'], args['remember_me'])
+ uid2 = webuser.getUid(req)
+ if uid2 == -1:
+ webuser.logoutUser(req)
+ return webuser.page_not_authorized(req, "../youraccount/login?ln=%s" % args['ln'], uid=uid,
+ navmenuid='youraccount')
+
+ # login successful!
+ if args['referer']:
+ redirect_to_url(req, args['referer'])
+ else:
+ return self.display(req, form)
+ else:
+ mess = CFG_WEBACCESS_WARNING_MSGS[msgcode] % cgi.escape(args['login_method'])
+ if msgcode == 14:
+ if webuser.username_exists_p(args['p_un']):
+ mess = CFG_WEBACCESS_WARNING_MSGS[15] % cgi.escape(args['login_method'])
+ act = '/youraccount/login%s' % make_canonical_urlargd({'ln' : args['ln'], 'referer' : args['referer']}, {})
+ return page(title=_("Login"),
+ body=webaccount.perform_back(mess, act, _("login"), args['ln']),
+ navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
+ description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
+ keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
+ uid=uid,
+ req=req,
+ secure_page_p = 1,
+ language=args['ln'],
+ lastupdated=__lastupdated__,
+ navmenuid='youraccount')
+
+
+
def login(self, req, form):
args = wash_urlargd(form, {
'p_un': (str, None),
'p_pw': (str, None),
'login_method': (str, None),
'action': (str, ''),
'remember_me' : (str, ''),
'referer': (str, '')})
# sanity checks:
args['login_method'] = wash_login_method(args['login_method'])
if args['p_un']:
args['p_un'] = args['p_un'].strip()
args['remember_me'] = args['remember_me'] != ''
locals().update(args)
if CFG_ACCESS_CONTROL_LEVEL_SITE > 0:
return webuser.page_not_authorized(req, "../youraccount/login?ln=%s" % args['ln'],
navmenuid='youraccount')
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
- apache_msg = ""
if args['action']:
cookie = args['action']
try:
action, arguments = mail_cookie_check_authorize_action(cookie)
- apache_msg = make_apache_message(action, arguments, args['referer'])
-
- # FIXME: Temporary Hack to help CDS current migration
- if CFG_CERN_SITE:
- roles = make_list_apache_firerole(action, arguments)
- if len(roles) == 1:
- # There's only one role enabled to see this collection
- # Let's redirect to log to it!
- return redirect_to_url(req, '%s/%s' % (CFG_SITE_SECURE_URL, make_canonical_urlargd({'realm' : roles[0][0], 'referer' : args['referer']}, {})))
except InvenioWebAccessMailCookieError:
pass
if not CFG_EXTERNAL_AUTH_USING_SSO:
if args['p_un'] is None or not args['login_method']:
return page(title=_("Login"),
- body=webaccount.create_login_page_box(args['referer'], apache_msg, args['ln']),
+ body=webaccount.create_login_page_box(args['referer'], args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
(iden, args['p_un'], args['p_pw'], msgcode) = webuser.loginUser(req, args['p_un'], args['p_pw'], args['login_method'])
else:
# Fake parameters for p_un & p_pw because SSO takes them from the environment
(iden, args['p_un'], args['p_pw'], msgcode) = webuser.loginUser(req, '', '', CFG_EXTERNAL_AUTH_USING_SSO)
args['remember_me'] = False
if len(iden)>0:
uid = webuser.update_Uid(req, args['p_un'], args['remember_me'])
uid2 = webuser.getUid(req)
if uid2 == -1:
webuser.logoutUser(req)
return webuser.page_not_authorized(req, "../youraccount/login?ln=%s" % args['ln'], uid=uid,
navmenuid='youraccount')
# login successful!
if args['referer']:
redirect_to_url(req, args['referer'])
else:
return self.display(req, form)
else:
mess = CFG_WEBACCESS_WARNING_MSGS[msgcode] % cgi.escape(args['login_method'])
if msgcode == 14:
if webuser.username_exists_p(args['p_un']):
mess = CFG_WEBACCESS_WARNING_MSGS[15] % cgi.escape(args['login_method'])
act = '/youraccount/login%s' % make_canonical_urlargd({'ln' : args['ln'], 'referer' : args['referer']}, {})
return page(title=_("Login"),
body=webaccount.perform_back(mess, act, _("login"), args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def register(self, req, form):
args = wash_urlargd(form, {
'p_nickname': (str, None),
'p_email': (str, None),
'p_pw': (str, None),
'p_pw2': (str, None),
'action': (str, "login"),
'referer': (str, "")})
if CFG_ACCESS_CONTROL_LEVEL_SITE > 0:
return webuser.page_not_authorized(req, "../youraccount/register?ln=%s" % args['ln'],
navmenuid='youraccount')
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if args['p_nickname'] is None or args['p_email'] is None:
return page(title=_("Register"),
body=webaccount.create_register_page_box(args['referer'], args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description=_("%s Personalize, Main page") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
mess = ""
act = ""
if args['p_pw'] == args['p_pw2']:
ruid = webuser.registerUser(req, args['p_email'], args['p_pw'],
args['p_nickname'], ln=args['ln'])
else:
ruid = -2
if ruid == 0:
mess = _("Your account has been successfully created.")
title = _("Account created")
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1:
mess += " " + _("In order to confirm its validity, an email message containing an account activation key has been sent to the given email address.")
mess += " " + _("Please follow instructions presented there in order to complete the account registration process.")
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1:
mess += " " + _("A second email will be sent when the account has been activated and can be used.")
elif CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT != 1:
uid = webuser.update_Uid(req, args['p_email'])
mess += " " + _("You can now access your %(x_url_open)saccount%(x_url_close)s.") %\
{'x_url_open': '',
'x_url_close': ''}
elif ruid == -2:
mess = _("Both passwords must match.")
mess += " " + _("Please try again.")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
elif ruid == 1:
mess = _("Supplied email address %s is invalid.") % cgi.escape(args['p_email'])
mess += " " + _("Please try again.")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
elif ruid == 2:
mess = _("Desired nickname %s is invalid.") % cgi.escape(args['p_nickname'])
mess += " " + _("Please try again.")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
elif ruid == 3:
mess = _("Supplied email address %s already exists in the database.") % cgi.escape(args['p_email'])
mess += " " + websession_templates.tmpl_lost_your_password_teaser(args['ln'])
mess += " " + _("Or please try again.")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
elif ruid == 4:
mess = _("Desired nickname %s already exists in the database.") % cgi.escape(args['p_nickname'])
mess += " " + _("Please try again.")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
elif ruid == 5:
mess = _("Users cannot register themselves, only admin can register them.")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
elif ruid == 6:
mess = _("The site is having troubles in sending you an email for confirming your email address.") + _("The error has been logged and will be taken in consideration as soon as possible.")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
else:
# this should never happen
mess = _("Internal Error")
act = "/youraccount/register?ln=%s" % args['ln']
title = _("Registration failure")
return page(title=title,
body=webaccount.perform_back(mess,act, _("register"), args['ln']),
navtrail="""""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """""",
description=_("%s Personalize, Main page") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
class WebInterfaceYourTicketsPages(WebInterfaceDirectory):
#support for /yourtickets url
_exports = ['', 'display']
def __call__(self, req, form):
#if there is no trailing slash
self.index(req, form)
def index(self, req, form):
#take all the parameters..
unparsed_uri = req.unparsed_uri
qstr = ""
if unparsed_uri.count('?') > 0:
dummy, qstr = unparsed_uri.split('?')
qstr = '?'+qstr
redirect_to_url(req, '/yourtickets/display'+qstr)
def display(self, req, form):
#show tickets for this user
argd = wash_urlargd(form, {'ln': (str, ''), 'start': (int, 1) })
uid = webuser.getUid(req)
ln = argd['ln']
start = argd['start']
_ = gettext_set_language(ln)
body = bibcatalog_templates.tmpl_your_tickets(uid, ln, start)
return page(title=_("Your tickets"),
body=body,
navtrail="""""" % (CFG_SITE_SECURE_URL, argd['ln']) + _("Your Account") + """""",
uid=uid,
req=req,
language=argd['ln'],
lastupdated=__lastupdated__)
class WebInterfaceYourGroupsPages(WebInterfaceDirectory):
_exports = ['', 'display', 'create', 'join', 'leave', 'edit', 'members']
def index(self, req, form):
redirect_to_url(req, '/yourgroups/display')
def display(self, req, form):
"""
Displays groups the user is admin of
and the groups the user is member of(but not admin)
@param ln: language
@return: the page for all the groups
"""
argd = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/display",
navmenuid='yourgroups')
user_info = webuser.collect_user_info(req)
if not user_info['precached_usegroups']:
return webuser.page_not_authorized(req, "../", \
text = _("You are not authorized to use groups."))
(body, errors, warnings) = webgroup.perform_request_groups_display(uid=uid,
ln=argd['ln'])
return page(title = _("Your Groups"),
body = body,
navtrail = webgroup.get_navtrail(argd['ln']),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def create(self, req, form):
"""create(): interface for creating a new group
@param group_name: : name of the new webgroup.Must be filled
@param group_description: : description of the new webgroup.(optionnal)
@param join_policy: : join policy of the new webgroup.Must be chosen
@param *button: which button was pressed
@param ln: language
@return: the compose page Create group
"""
argd = wash_urlargd(form, {'group_name': (str, ""),
'group_description': (str, ""),
'join_policy': (str, ""),
'create_button':(str, ""),
'cancel':(str, "")
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/create",
navmenuid='yourgroups')
user_info = webuser.collect_user_info(req)
if not user_info['precached_usegroups']:
return webuser.page_not_authorized(req, "../", \
text = _("You are not authorized to use groups."))
if argd['cancel']:
url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['create_button'] :
(body, errors, warnings)= webgroup.perform_request_create_group(uid=uid,
group_name=argd['group_name'],
group_description=argd['group_description'],
join_policy=argd['join_policy'],
ln = argd['ln'])
else:
(body, errors, warnings) = webgroup.perform_request_input_create_group(group_name=argd['group_name'],
group_description=argd['group_description'],
join_policy=argd['join_policy'],
ln=argd['ln'])
title = _("Create new group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def join(self, req, form):
"""join(): interface for joining a new group
@param grpID: : list of the group the user wants to become a member.
The user must select only one group.
@param group_name: : will search for groups matching group_name
@param *button: which button was pressed
@param ln: language
@return: the compose page Join group
"""
argd = wash_urlargd(form, {'grpID':(list, []),
'group_name':(str, ""),
'find_button':(str, ""),
'join_button':(str, ""),
'cancel':(str, "")
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/join",
navmenuid='yourgroups')
user_info = webuser.collect_user_info(req)
if not user_info['precached_usegroups']:
return webuser.page_not_authorized(req, "../", \
text = _("You are not authorized to use groups."))
if argd['cancel']:
url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['join_button']:
search = 0
if argd['group_name']:
search = 1
(body, errors, warnings) = webgroup.perform_request_join_group(uid,
argd['grpID'],
argd['group_name'],
search,
argd['ln'])
else:
search = 0
if argd['find_button']:
search = 1
(body, errors, warnings) = webgroup.perform_request_input_join_group(uid,
argd['group_name'],
search,
ln=argd['ln'])
title = _("Join New Group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def leave(self, req, form):
"""leave(): interface for leaving a group
@param grpID: : group the user wants to leave.
@param group_name: : name of the group the user wants to leave
@param *button: which button was pressed
@param confirmed: : the user is first asked to confirm
@param ln: language
@return: the compose page Leave group
"""
argd = wash_urlargd(form, {'grpID':(int, 0),
'group_name':(str, ""),
'leave_button':(str, ""),
'cancel':(str, ""),
'confirmed': (int, 0)
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/leave",
navmenuid='yourgroups')
user_info = webuser.collect_user_info(req)
if not user_info['precached_usegroups']:
return webuser.page_not_authorized(req, "../", \
text = _("You are not authorized to use groups."))
if argd['cancel']:
url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['leave_button']:
(body, errors, warnings) = webgroup.perform_request_leave_group(uid,
argd['grpID'],
argd['confirmed'],
argd['ln'])
else:
(body, errors, warnings) = webgroup.perform_request_input_leave_group(uid=uid,
ln=argd['ln'])
title = _("Leave Group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def edit(self, req, form):
"""edit(): interface for editing group
@param grpID: : group ID
@param group_name: : name of the new webgroup.Must be filled
@param group_description: : description of the new webgroup.(optionnal)
@param join_policy: : join policy of the new webgroup.Must be chosen
@param update: button update group pressed
@param delete: button delete group pressed
@param cancel: button cancel pressed
@param confirmed: : the user is first asked to confirm before deleting
@param ln: language
@return: the main page displaying all the groups
"""
argd = wash_urlargd(form, {'grpID': (int, 0),
'update': (str, ""),
'cancel': (str, ""),
'delete': (str, ""),
'group_name': (str, ""),
'group_description': (str, ""),
'join_policy': (str, ""),
'confirmed': (int, 0)
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/display",
navmenuid='yourgroups')
user_info = webuser.collect_user_info(req)
if not user_info['precached_usegroups']:
return webuser.page_not_authorized(req, "../", \
text = _("You are not authorized to use groups."))
if argd['cancel']:
url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
elif argd['delete']:
(body, errors, warnings) = webgroup.perform_request_delete_group(uid=uid,
grpID=argd['grpID'],
confirmed=argd['confirmed'])
elif argd['update']:
(body, errors, warnings) = webgroup.perform_request_update_group(uid= uid,
grpID=argd['grpID'],
group_name=argd['group_name'],
group_description=argd['group_description'],
join_policy=argd['join_policy'],
ln=argd['ln'])
else :
(body, errors, warnings)= webgroup.perform_request_edit_group(uid=uid,
grpID=argd['grpID'],
ln=argd['ln'])
title = _("Edit Group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def members(self, req, form):
"""member(): interface for managing members of a group
@param grpID: : group ID
@param add_member: button add_member pressed
@param remove_member: button remove_member pressed
@param reject_member: button reject__member pressed
@param delete: button delete group pressed
@param member_id: : ID of the existing member selected
@param pending_member_id: : ID of the pending member selected
@param cancel: button cancel pressed
@param info: : info about last user action
@param ln: language
@return: the same page with data updated
"""
argd = wash_urlargd(form, {'grpID': (int, 0),
'cancel': (str, ""),
'add_member': (str, ""),
'remove_member': (str, ""),
'reject_member': (str, ""),
'member_id': (int, 0),
'pending_member_id': (int, 0)
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/display",
navmenuid='yourgroups')
user_info = webuser.collect_user_info(req)
if not user_info['precached_usegroups']:
return webuser.page_not_authorized(req, "../", \
text = _("You are not authorized to use groups."))
if argd['cancel']:
url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['remove_member']:
(body, errors, warnings) = webgroup.perform_request_remove_member(uid=uid,
grpID=argd['grpID'],
member_id=argd['member_id'],
ln=argd['ln'])
elif argd['reject_member']:
(body, errors, warnings) = webgroup.perform_request_reject_member(uid=uid,
grpID=argd['grpID'],
user_id=argd['pending_member_id'],
ln=argd['ln'])
elif argd['add_member']:
(body, errors, warnings) = webgroup.perform_request_add_member(uid=uid,
grpID=argd['grpID'],
user_id=argd['pending_member_id'],
ln=argd['ln'])
else:
(body, errors, warnings)= webgroup.perform_request_manage_member(uid=uid,
grpID=argd['grpID'],
ln=argd['ln'])
title = _("Edit group members")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def wash_login_method(login_method):
"""
Wash the login_method parameter that came from the web input form.
@param login_method: Wanted login_method value as it came from the
web input form.
@type login_method: string
@return: Washed version of login_method. If the login_method
value is valid, then return it. If it is not valid, then
return `Local' (the default login method).
@rtype: string
@warning: Beware, 'Local' is hardcoded here!
"""
- if login_method in CFG_EXTERNAL_AUTHENTICATION.keys():
+ if login_method in CFG_EXTERNAL_AUTHENTICATION:
return login_method
else:
return 'Local'
diff --git a/modules/websession/lib/webuser.py b/modules/websession/lib/webuser.py
index b8c427cb6..2320d60f5 100644
--- a/modules/websession/lib/webuser.py
+++ b/modules/websession/lib/webuser.py
@@ -1,1298 +1,1187 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
This file implements all methods necessary for working with users and
sessions in Invenio. Contains methods for logging/registration
when a user log/register into the system, checking if it is a guest
user or not.
At the same time this presents all the stuff it could need with
sessions managements, working with websession.
It also contains Apache-related user authentication stuff.
"""
__revision__ = "$Id$"
from invenio import webinterface_handler_config as apache
import cgi
import urllib
import urlparse
from socket import gaierror
import os
import crypt
import socket
import smtplib
import re
import random
import datetime
import base64
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_ACCESS_CONTROL_LEVEL_GUESTS, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN, \
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT, \
CFG_APACHE_GROUP_FILE, \
CFG_APACHE_PASSWORD_FILE, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_TMPDIR, \
CFG_SITE_URL, \
CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS, \
CFG_CERN_SITE, \
CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL
try:
from invenio.session import get_session
except ImportError:
pass
from invenio.dbquery import run_sql, OperationalError, \
serialize_via_marshal, deserialize_via_marshal
from invenio.access_control_admin import acc_get_role_id, acc_get_action_roles, acc_get_action_id, acc_is_user_in_role, acc_find_possible_activities
from invenio.access_control_mailcookie import mail_cookie_create_mail_activation
from invenio.access_control_firerole import acc_firerole_check_user, load_role_definition
from invenio.access_control_config import SUPERADMINROLE, CFG_EXTERNAL_AUTH_USING_SSO
from invenio.messages import gettext_set_language, wash_languages, wash_language
from invenio.mailutils import send_email
from invenio.errorlib import register_exception
from invenio.webgroup_dblayer import get_groups
from invenio.external_authentication import InvenioWebAccessExternalAuthError
from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION, \
- CFG_WEBACCESS_MSGS, CFG_WEBACCESS_WARNING_MSGS
+ CFG_WEBACCESS_MSGS, CFG_WEBACCESS_WARNING_MSGS, CFG_EXTERNAL_AUTH_DEFAULT
import invenio.template
tmpl = invenio.template.load('websession')
re_invalid_nickname = re.compile(""".*[,'@]+.*""")
# pylint: disable=C0301
def createGuestUser():
"""Create a guest user , insert into user null values in all fields
createGuestUser() -> GuestUserID
"""
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
try:
return run_sql("insert into user (email, note) values ('', '1')")
except OperationalError:
return None
else:
try:
return run_sql("insert into user (email, note) values ('', '0')")
except OperationalError:
return None
def page_not_authorized(req, referer='', uid='', text='', navtrail='', ln=CFG_SITE_LANG,
navmenuid=""):
"""Show error message when user is not authorized to do something.
@param referer: in case the displayed message propose a login link, this
is the url to return to after logging in. If not specified it is guessed
from req.
@param uid: the uid of the user. If not specified it is guessed from req.
@param text: the message to be displayed. If not specified it will be
guessed from the context.
"""
from invenio.webpage import page
_ = gettext_set_language(ln)
if not referer:
referer = req.unparsed_uri
if not CFG_ACCESS_CONTROL_LEVEL_SITE:
title = CFG_WEBACCESS_MSGS[5]
if not uid:
uid = getUid(req)
try:
res = run_sql("SELECT email FROM user WHERE id=%s AND note=1" % uid)
if res and res[0][0]:
if text:
body = text
else:
body = "%s %s" % (CFG_WEBACCESS_WARNING_MSGS[9] % cgi.escape(res[0][0]),
("%s %s" % (CFG_WEBACCESS_MSGS[0] % urllib.quote(referer), CFG_WEBACCESS_MSGS[1])))
else:
if text:
body = text
else:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 1:
body = CFG_WEBACCESS_MSGS[3]
else:
body = CFG_WEBACCESS_WARNING_MSGS[4] + CFG_WEBACCESS_MSGS[2]
except OperationalError, e:
body = _("Database problem") + ': ' + str(e)
elif CFG_ACCESS_CONTROL_LEVEL_SITE == 1:
title = CFG_WEBACCESS_MSGS[8]
body = "%s %s" % (CFG_WEBACCESS_MSGS[7], CFG_WEBACCESS_MSGS[2])
elif CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
title = CFG_WEBACCESS_MSGS[6]
body = "%s %s" % (CFG_WEBACCESS_MSGS[4], CFG_WEBACCESS_MSGS[2])
return page(title=title,
language=ln,
uid=getUid(req),
body=body,
navtrail=navtrail,
req=req,
navmenuid=navmenuid)
-def getApacheUser(req):
- """Return the ApacheUser taking it from the cookie of the request."""
- session = get_session(req)
- return session.get('apache_user')
-
def getUid(req):
"""Return user ID taking it from the cookie of the request.
Includes control mechanism for the guest users, inserting in
the database table when need be, raising the cookie back to the
client.
User ID is set to 0 when client refuses cookie or we are in the
read-only site operation mode.
User ID is set to -1 when we are in the permission denied site
operation mode.
getUid(req) -> userId
"""
if hasattr(req, '_user_info'):
return req._user_info['uid']
if CFG_ACCESS_CONTROL_LEVEL_SITE == 1: return 0
if CFG_ACCESS_CONTROL_LEVEL_SITE == 2: return -1
guest = 0
session = get_session(req)
uid = session.get('uid', -1)
if uid == -1: # first time, so create a guest user
if CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
uid = session['uid'] = createGuestUser()
guest = 1
else:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
session['uid'] = 0
return 0
else:
return -1
else:
if not hasattr(req, '_user_info') and 'user_info' in session:
req._user_info = session['user_info']
req._user_info = collect_user_info(req, refresh=True)
if guest == 0:
guest = isGuestUser(uid)
if guest:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
return uid
elif CFG_ACCESS_CONTROL_LEVEL_GUESTS >= 1:
return -1
else:
res = run_sql("SELECT note FROM user WHERE id=%s", (uid, ))
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 0:
return uid
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1 and res and res[0][0] in [1, "1"]:
return uid
else:
return -1
-def setApacheUser(req, apache_user):
- """It sets the apache_user into the session, and raise the cookie to the client.
- """
- if hasattr(req, '_user_info'):
- del req._user_info
- session = get_session(req)
- session['apache_user'] = apache_user
- user_info = collect_user_info(req, login_time=True)
- session['user_info'] = user_info
- req._user_info = user_info
- session.save()
- return apache_user
-
def setUid(req, uid, remember_me=False):
"""It sets the userId into the session, and raise the cookie to the client.
"""
if hasattr(req, '_user_info'):
del req._user_info
session = get_session(req)
session['uid'] = uid
if remember_me:
session.set_timeout(86400)
session.set_remember_me()
if uid > 0:
user_info = collect_user_info(req, login_time=True)
session['user_info'] = user_info
req._user_info = user_info
else:
del session['user_info']
session.save()
return uid
def session_param_del(req, key):
"""
Remove a given key from the session.
"""
session = get_session(req)
del session[key]
session.save()
def session_param_set(req, key, value):
"""
Associate a VALUE to the session param KEY for the current session.
"""
session = get_session(req)
session[key] = value
session.save()
def session_param_get(req, key):
"""
Return session parameter value associated with session parameter KEY for the current session.
If the key doesn't exists raise KeyError.
"""
session = get_session(req)
return session[key]
def session_param_list(req):
"""
List all available session parameters.
"""
session = get_session(req)
return session.keys()
def get_last_login(uid):
"""Return the last_login datetime for uid if any, otherwise return the Epoch."""
res = run_sql('SELECT last_login FROM user WHERE id=%s', (uid, ), 1)
if res and res[0][0]:
return res[0][0]
else:
return datetime.datetime(1970, 1, 1)
def get_user_info(uid, ln=CFG_SITE_LANG):
"""Get infos for a given user.
@param uid: user id (int)
@return: tuple: (uid, nickname, display_name)
"""
_ = gettext_set_language(ln)
query = """SELECT id, nickname
FROM user
WHERE id=%s"""
res = run_sql(query, (uid, ))
if res:
if res[0]:
user = list(res[0])
if user[1]:
user.append(user[1])
else:
user[1] = str(user[0])
user.append(_("user") + ' #' + str(user[0]))
return tuple(user)
return (uid, '', _("N/A"))
def get_uid_from_email(email):
"""Return the uid corresponding to an email.
Return -1 when the email does not exists."""
try:
res = run_sql("SELECT id FROM user WHERE email=%s", (email, ))
if res:
return res[0][0]
else:
return -1
except OperationalError:
register_exception()
return -1
def isGuestUser(uid):
"""It Checks if the userId corresponds to a guestUser or not
isGuestUser(uid) -> boolean
"""
out = 1
try:
res = run_sql("SELECT email FROM user WHERE id=%s LIMIT 1", (uid,), 1)
if res:
if res[0][0]:
out = 0
except OperationalError:
register_exception()
return out
def isUserSubmitter(user_info):
"""Return True if the user is a submitter for something; False otherwise."""
u_email = get_email(user_info['uid'])
res = run_sql("SELECT email FROM sbmSUBMISSIONS WHERE email=%s LIMIT 1", (u_email,), 1)
return len(res) > 0
def isUserReferee(user_info):
"""Return True if the user is a referee for something; False otherwise."""
if CFG_CERN_SITE:
return True
else:
for (role_id, role_name, role_description) in acc_get_action_roles(acc_get_action_id('referee')):
if acc_is_user_in_role(user_info, role_id):
return True
return False
def isUserAdmin(user_info):
"""Return True if the user has some admin rights; False otherwise."""
return acc_find_possible_activities(user_info) != {}
def isUserSuperAdmin(user_info):
"""Return True if the user is superadmin; False otherwise."""
if run_sql("""SELECT r.id
FROM accROLE r LEFT JOIN user_accROLE ur
ON r.id = ur.id_accROLE
WHERE r.name = %s AND
ur.id_user = %s AND ur.expiration>=NOW() LIMIT 1""", (SUPERADMINROLE, user_info['uid']), 1):
return True
return acc_firerole_check_user(user_info, load_role_definition(acc_get_role_id(SUPERADMINROLE)))
def nickname_valid_p(nickname):
"""Check whether wanted NICKNAME supplied by the user is valid.
At the moment we just check whether it is not empty, does not
contain blanks or @, is not equal to `guest', etc.
This check relies on re_invalid_nickname regexp (see above)
Return 1 if nickname is okay, return 0 if it is not.
"""
if nickname and \
not(nickname.startswith(' ') or nickname.endswith(' ')) and \
nickname.lower() != 'guest':
if not re_invalid_nickname.match(nickname):
return 1
return 0
def email_valid_p(email):
"""Check whether wanted EMAIL address supplied by the user is valid.
At the moment we just check whether it contains '@' and whether
it doesn't contain blanks. We also check the email domain if
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN is set.
Return 1 if email is okay, return 0 if it is not.
"""
if (email.find("@") <= 0) or (email.find(" ") > 0):
return 0
elif CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN:
if not email.endswith(CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN):
return 0
return 1
def confirm_email(email):
"""Confirm the email. It returns None when there are problems, otherwise
it return the uid involved."""
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 0:
activated = 1
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
activated = 0
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2:
return -1
run_sql('UPDATE user SET note=%s where email=%s', (activated, email))
res = run_sql('SELECT id FROM user where email=%s', (email, ))
if res:
if CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS:
send_new_admin_account_warning(email, CFG_SITE_ADMIN_EMAIL)
return res[0][0]
else:
return None
def registerUser(req, email, passw, nickname, register_without_nickname=False,
login_method=None, ln=CFG_SITE_LANG):
"""Register user with the desired values of NICKNAME, EMAIL and
PASSW.
If REGISTER_WITHOUT_NICKNAME is set to True, then ignore
desired NICKNAME and do not set any. This is suitable for
external authentications so that people can login without
having to register an internal account first.
Return 0 if the registration is successful, 1 if email is not
valid, 2 if nickname is not valid, 3 if email is already in the
database, 4 if nickname is already in the database, 5 when
users cannot register themselves because of the site policy, 6 when the
site is having problem contacting the user.
If login_method is None or is equal to the key corresponding to local
authentication, then CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS is taken
in account for deciding the behaviour about registering.
"""
# is email valid?
email = email.lower()
if not email_valid_p(email):
return 1
_ = gettext_set_language(ln)
# is email already taken?
res = run_sql("SELECT email FROM user WHERE email=%s", (email,))
if len(res) > 0:
return 3
if register_without_nickname:
# ignore desired nick and use default empty string one:
nickname = ""
else:
# is nickname valid?
if not nickname_valid_p(nickname):
return 2
# is nickname already taken?
res = run_sql("SELECT nickname FROM user WHERE nickname=%s", (nickname,))
if len(res) > 0:
return 4
activated = 1 # By default activated
- if not login_method or not CFG_EXTERNAL_AUTHENTICATION[login_method][0]: # local login
+ if not login_method or not CFG_EXTERNAL_AUTHENTICATION[login_method]: # local login
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2:
return 5
elif CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT:
activated = 2 # Email confirmation required
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1:
activated = 0 # Administrator confirmation required
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT:
address_activation_key = mail_cookie_create_mail_activation(email)
ip_address = req.remote_host or req.remote_ip
try:
if not send_email(CFG_SITE_SUPPORT_EMAIL, email, _("Account registration at %s") % CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
tmpl.tmpl_account_address_activation_email_body(email,
address_activation_key, ip_address, ln)):
return 1
except (smtplib.SMTPException, socket.error):
return 6
# okay, go on and register the user:
user_preference = get_default_user_preferences()
uid = run_sql("INSERT INTO user (nickname, email, password, note, settings, last_login) "
"VALUES (%s,%s,AES_ENCRYPT(email,%s),%s,%s, NOW())",
(nickname, email, passw, activated, serialize_via_marshal(user_preference)))
if activated == 1: # Ok we consider the user as logged in :-)
setUid(req, uid)
return 0
def updateDataUser(uid, email, nickname):
"""
Update user data. Used when a user changed his email or password
or nickname.
"""
email = email.lower()
if email == 'guest':
return 0
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 2:
run_sql("update user set email=%s where id=%s", (email, uid))
if nickname and nickname != '':
run_sql("update user set nickname=%s where id=%s", (nickname, uid))
return 1
def updatePasswordUser(uid, password):
"""Update the password of a user."""
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 3:
run_sql("update user set password=AES_ENCRYPT(email,%s) where id=%s", (password, uid))
return 1
def loginUser(req, p_un, p_pw, login_method):
"""It is a first simple version for the authentication of user. It returns the id of the user,
for checking afterwards if the login is correct
"""
# p_un passed may be an email or a nickname:
p_email = get_email_from_username(p_un)
# go on with the old stuff based on p_email:
- if not CFG_EXTERNAL_AUTHENTICATION.has_key(login_method):
+ if not login_method in CFG_EXTERNAL_AUTHENTICATION:
return ([], p_email, p_pw, 12)
- if CFG_EXTERNAL_AUTHENTICATION[login_method][0]: # External Authenthication
+ if CFG_EXTERNAL_AUTHENTICATION[login_method]: # External Authenthication
try:
- p_email = CFG_EXTERNAL_AUTHENTICATION[login_method][0].auth_user(p_email, p_pw, req) or CFG_EXTERNAL_AUTHENTICATION[login_method][0].auth_user(p_un, p_pw, req) ## We try to login with either the email of the nickname
+ 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(alert_admin=True)
+ 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][0].enforce_external_nicknames:
+ if CFG_EXTERNAL_AUTHENTICATION[login_method].enforce_external_nicknames:
try: # Let's discover the external nickname!
- p_nickname = CFG_EXTERNAL_AUTHENTICATION[login_method][0].fetch_user_nickname(p_email, p_pw, req)
+ p_nickname = CFG_EXTERNAL_AUTHENTICATION[login_method].fetch_user_nickname(p_email, p_pw, req)
except (AttributeError, NotImplementedError):
pass
except:
- register_exception(alert_admin=True)
+ register_exception(req=req, alert_admin=True)
raise
res = registerUser(req, p_email, p_pw_local, p_nickname,
register_without_nickname=p_nickname == '',
login_method=login_method)
if res == 4 or res == 2: # The nickname was already taken
res = registerUser(req, p_email, p_pw_local, '',
register_without_nickname=True,
login_method=login_method)
+ query_result = run_sql("SELECT id from user where email=%s", (p_email,))
elif res == 0: # Everything was ok, with or without nickname.
query_result = run_sql("SELECT id from user where email=%s", (p_email,))
elif res == 6: # error in contacting the user via email
return([], p_email, p_pw_local, 19)
else:
return([], p_email, p_pw_local, 13)
try:
- groups = CFG_EXTERNAL_AUTHENTICATION[login_method][0].fetch_user_groups_membership(p_email, p_pw, req)
+ groups = CFG_EXTERNAL_AUTHENTICATION[login_method].fetch_user_groups_membership(p_email, p_pw, req)
# groups is a dictionary {group_name : group_description,}
new_groups = {}
for key, value in groups.items():
new_groups[key + " [" + str(login_method) + "]"] = value
groups = new_groups
except (AttributeError, NotImplementedError):
pass
except:
- register_exception(alert_admin=True)
+ register_exception(req=req, alert_admin=True)
return([], p_email, p_pw, 16)
else: # Groups synchronization
- if groups != 0:
+ if groups:
userid = query_result[0][0]
from invenio.webgroup import synchronize_external_groups
synchronize_external_groups(userid, groups, login_method)
user_prefs = get_user_preferences(query_result[0][0])
- if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4:
- # Let's prevent the user to switch login_method
- if user_prefs.has_key("login_method") and \
- user_prefs["login_method"] != login_method:
- return([], p_email, p_pw, 11)
- user_prefs["login_method"] = login_method
+ if not CFG_EXTERNAL_AUTHENTICATION[login_method]:
+ ## I.e. if the login method is not of robot type:
+ if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4:
+ # Let's prevent the user to switch login_method
+ if user_prefs.has_key("login_method") and \
+ user_prefs["login_method"] != login_method:
+ return([], p_email, p_pw, 11)
+ user_prefs["login_method"] = login_method
# Cleaning external settings
for key in user_prefs.keys():
if key.startswith('EXTERNAL_'):
del user_prefs[key]
try:
# Importing external settings
- new_prefs = CFG_EXTERNAL_AUTHENTICATION[login_method][0].fetch_user_preferences(p_email, p_pw, req)
+ new_prefs = CFG_EXTERNAL_AUTHENTICATION[login_method].fetch_user_preferences(p_email, p_pw, req)
for key, value in new_prefs.items():
user_prefs['EXTERNAL_' + key] = value
except (AttributeError, NotImplementedError):
pass
except InvenioWebAccessExternalAuthError:
- register_exception(alert_admin=True)
+ register_exception(req=req, alert_admin=True)
return([], p_email, p_pw, 16)
# Storing settings
set_user_preferences(query_result[0][0], user_prefs)
else:
return ([], p_un, p_pw, 10)
else: # Internal Authenthication
if not p_pw:
p_pw = ''
query_result = run_sql("SELECT id,email,note from user where email=%s and password=AES_ENCRYPT(email,%s)", (p_email, p_pw,))
if query_result:
#FIXME drop external groups and settings
note = query_result[0][2]
if note == '1': # Good account
preferred_login_method = get_user_preferences(query_result[0][0])['login_method']
p_email = query_result[0][1].lower()
if login_method != preferred_login_method:
- if CFG_EXTERNAL_AUTHENTICATION.has_key(preferred_login_method):
+ if preferred_login_method in CFG_EXTERNAL_AUTHENTICATION:
return ([], p_email, p_pw, 11)
elif note == '2': # Email address need to be confirmed by user
return ([], p_email, p_pw, 17)
elif note == '0': # Account need to be confirmed by administrator
return ([], p_email, p_pw, 18)
else:
return ([], p_email, p_pw, 14)
# Login successful! Updating the last access time
run_sql("UPDATE user SET last_login=NOW() WHERE email=%s", (p_email, ))
return (query_result, p_email, p_pw, 0)
def drop_external_settings(userId):
"""Drop the external (EXTERNAL_) settings of userid."""
prefs = get_user_preferences(userId)
for key in prefs.keys():
if key.startswith('EXTERNAL_'):
del prefs[key]
set_user_preferences(userId, prefs)
def logoutUser(req):
"""It logout the user of the system, creating a guest user.
"""
session = get_session(req)
if CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
uid = createGuestUser()
session['uid'] = uid
session.save()
else:
uid = 0
session.invalidate()
if hasattr(req, '_user_info'):
delattr(req, '_user_info')
return uid
def username_exists_p(username):
"""Check if USERNAME exists in the system. Username may be either
nickname or email.
Return 1 if it does exist, 0 if it does not.
"""
if username == "":
# return not exists if asked for guest users
return 0
res = run_sql("SELECT email FROM user WHERE email=%s", (username,)) + \
run_sql("SELECT email FROM user WHERE nickname=%s", (username,))
if len(res) > 0:
return 1
return 0
def emailUnique(p_email):
"""Check if the email address only exists once. If yes, return userid, if not, -1
"""
query_result = run_sql("select id, email from user where email=%s", (p_email,))
if len(query_result) == 1:
return query_result[0][0]
elif len(query_result) == 0:
return 0
return -1
def nicknameUnique(p_nickname):
"""Check if the nickname only exists once. If yes, return userid, if not, -1
"""
query_result = run_sql("select id, nickname from user where nickname=%s", (p_nickname,))
if len(query_result) == 1:
return query_result[0][0]
elif len(query_result) == 0:
return 0
return -1
def update_Uid(req, p_email, remember_me=False):
"""It updates the userId of the session. It is used when a guest user is logged in succesfully in the system with a given email and password.
As a side effect it will discover all the restricted collection to which the user has right to
"""
query_ID = int(run_sql("select id from user where email=%s",
(p_email,))[0][0])
setUid(req, query_ID, remember_me)
return query_ID
def send_new_admin_account_warning(new_account_email, send_to, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account new_account_email."""
_ = gettext_set_language(ln)
sub = _("New account on") + " '%s'" % CFG_SITE_NAME
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
sub += " - " + _("PLEASE ACTIVATE")
body = _("A new account has been created on") + " '%s'" % CFG_SITE_NAME
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
body += _(" and is awaiting activation")
body += ":\n\n"
body += _(" Username/Email") + ": %s\n\n" % new_account_email
body += _("You can approve or reject this account request at") + ": %s/admin/webaccess/webaccessadmin.py/manageaccounts\n" % CFG_SITE_URL
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, subject=sub, content=body)
def get_email(uid):
"""Return email address of the user uid. Return string 'guest' in case
the user is not found."""
out = "guest"
res = run_sql("SELECT email FROM user WHERE id=%s", (uid,), 1)
if res and res[0][0]:
out = res[0][0].lower()
return out
def get_email_from_username(username):
"""Return email address of the user corresponding to USERNAME.
The username may be either nickname or email. Return USERNAME
untouched if not found in the database or if found several
matching entries.
"""
if username == '':
return ''
out = username
res = run_sql("SELECT email FROM user WHERE email=%s", (username,), 1) + \
run_sql("SELECT email FROM user WHERE nickname=%s", (username,), 1)
if res and len(res) == 1:
out = res[0][0].lower()
return out
#def get_password(uid):
#"""Return password of the user uid. Return None in case
#the user is not found."""
#out = None
#res = run_sql("SELECT password FROM user WHERE id=%s", (uid,), 1)
#if res and res[0][0] != None:
#out = res[0][0]
#return out
def get_nickname(uid):
"""Return nickname of the user uid. Return None in case
the user is not found."""
out = None
res = run_sql("SELECT nickname FROM user WHERE id=%s", (uid,), 1)
if res and res[0][0]:
out = res[0][0]
return out
def get_nickname_or_email(uid):
"""Return nickname (preferred) or the email address of the user uid.
Return string 'guest' in case the user is not found."""
out = "guest"
res = run_sql("SELECT nickname, email FROM user WHERE id=%s", (uid,), 1)
if res and res[0]:
if res[0][0]:
out = res[0][0]
elif res[0][1]:
out = res[0][1].lower()
return out
def create_userinfobox_body(req, uid, language="en"):
"""Create user info box body for user UID in language LANGUAGE."""
if req:
if req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on':
url_referer = CFG_SITE_SECURE_URL + req.unparsed_uri
else:
url_referer = CFG_SITE_URL + req.unparsed_uri
if '/youraccount/logout' in url_referer:
url_referer = ''
else:
url_referer = CFG_SITE_URL
user_info = collect_user_info(req)
try:
return tmpl.tmpl_create_userinfobox(ln=language,
url_referer=url_referer,
guest = isGuestUser(uid),
username = get_nickname_or_email(uid),
submitter = user_info['precached_viewsubmissions'],
referee = user_info['precached_useapprove'],
admin = user_info['precached_useadmin'],
usebaskets = user_info['precached_usebaskets'],
usemessages = user_info['precached_usemessages'],
usealerts = user_info['precached_usealerts'],
usegroups = user_info['precached_usegroups'],
useloans = user_info['precached_useloans'],
usestats = user_info['precached_usestats']
)
except OperationalError:
return ""
def create_useractivities_menu(req, uid, navmenuid, ln="en"):
"""Create user activities menu.
@param req: request object
@param uid: user id
@type uid: int
@param navmenuid: the section of the website this page belongs (search, submit, baskets, etc.)
@type navmenuid: string
@param ln: language
@type ln: string
@return: HTML menu of the user activities
@rtype: string
"""
if req:
if req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on':
url_referer = CFG_SITE_SECURE_URL + req.unparsed_uri
else:
url_referer = CFG_SITE_URL + req.unparsed_uri
if '/youraccount/logout' in url_referer:
url_referer = ''
else:
url_referer = CFG_SITE_URL
user_info = collect_user_info(req)
is_user_menu_selected = False
if navmenuid == 'personalize' or \
navmenuid.startswith('your') and \
navmenuid != 'youraccount':
is_user_menu_selected = True
try:
return tmpl.tmpl_create_useractivities_menu(
ln=ln,
selected=is_user_menu_selected,
url_referer=url_referer,
guest = isGuestUser(uid),
username = get_nickname_or_email(uid),
submitter = user_info['precached_viewsubmissions'],
referee = user_info['precached_useapprove'],
admin = user_info['precached_useadmin'],
usebaskets = user_info['precached_usebaskets'],
usemessages = user_info['precached_usemessages'],
usealerts = user_info['precached_usealerts'],
usegroups = user_info['precached_usegroups'],
useloans = user_info['precached_useloans'],
usestats = user_info['precached_usestats']
)
except OperationalError:
return ""
def create_adminactivities_menu(req, uid, navmenuid, ln="en"):
"""Create admin activities menu.
@param req: request object
@param uid: user id
@type uid: int
@param navmenuid: the section of the website this page belongs (search, submit, baskets, etc.)
@type navmenuid: string
@param ln: language
@type ln: string
@return: HTML menu of the user activities
@rtype: string
"""
_ = gettext_set_language(ln)
if req:
if req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on':
url_referer = CFG_SITE_SECURE_URL + req.unparsed_uri
else:
url_referer = CFG_SITE_URL + req.unparsed_uri
if '/youraccount/logout' in url_referer:
url_referer = ''
else:
url_referer = CFG_SITE_URL
user_info = collect_user_info(req)
activities = acc_find_possible_activities(user_info, ln)
# For BibEdit and BibDocFile menu items, take into consideration
# current record whenever possible
if activities.has_key(_("Run Record Editor")) or \
activities.has_key(_("Run Document File Manager")) and \
user_info['uri'].startswith('/record/'):
try:
# Get record ID and try to cast it to an int
current_record_id = int(urlparse.urlparse(user_info['uri'])[2].split('/')[2])
except:
pass
else:
if activities.has_key(_("Run Record Editor")):
activities[_("Run Record Editor")] = activities[_("Run Record Editor")] + '&#state=edit&recid=' + str(current_record_id)
if activities.has_key(_("Run File Manager")):
activities[_("Run File Manager")] = activities[_("Run File Manager")] + '&recid=' + str(current_record_id)
try:
return tmpl.tmpl_create_adminactivities_menu(
ln=ln,
selected=navmenuid == 'admin',
url_referer=url_referer,
guest = isGuestUser(uid),
username = get_nickname_or_email(uid),
submitter = user_info['precached_viewsubmissions'],
referee = user_info['precached_useapprove'],
admin = user_info['precached_useadmin'],
usebaskets = user_info['precached_usebaskets'],
usemessages = user_info['precached_usemessages'],
usealerts = user_info['precached_usealerts'],
usegroups = user_info['precached_usegroups'],
useloans = user_info['precached_useloans'],
usestats = user_info['precached_usestats'],
activities = activities
)
except OperationalError:
return ""
def list_registered_users():
"""List all registered users."""
return run_sql("SELECT id,email FROM user where email!=''")
def list_users_in_role(role):
"""List all users of a given role (see table accROLE)
@param role: role of user (string)
@return: list of uids
"""
res = run_sql("""SELECT uacc.id_user
FROM user_accROLE uacc JOIN accROLE acc
ON uacc.id_accROLE=acc.id
WHERE acc.name=%s""",
(role,))
if res:
return map(lambda x: int(x[0]), res)
return []
def list_users_in_roles(role_list):
"""List all users of given roles (see table accROLE)
@param role_list: list of roles [string]
@return: list of uids
"""
if not(type(role_list) is list or type(role_list) is tuple):
role_list = [role_list]
query = """SELECT DISTINCT(uacc.id_user)
FROM user_accROLE uacc JOIN accROLE acc
ON uacc.id_accROLE=acc.id
"""
query_addons = ""
query_params = ()
if len(role_list) > 0:
query_params = role_list
query_addons = " WHERE "
for role in role_list[:-1]:
query_addons += "acc.name=%s OR "
query_addons += "acc.name=%s"
res = run_sql(query + query_addons, query_params)
if res:
return map(lambda x: int(x[0]), res)
return []
def get_uid_based_on_pref(prefname, prefvalue):
"""get the user's UID based where his/her preference prefname has value prefvalue in preferences"""
prefs = run_sql("SELECT id, settings FROM user WHERE settings is not NULL")
the_uid = None
for pref in prefs:
try:
settings = deserialize_via_marshal(pref[1])
if (settings.has_key(prefname)) and (settings[prefname] == prefvalue):
the_uid = pref[0]
except:
pass
return the_uid
def get_user_preferences(uid):
pref = run_sql("SELECT id, settings FROM user WHERE id=%s", (uid,))
if pref:
try:
return deserialize_via_marshal(pref[0][1])
except:
pass
return get_default_user_preferences() # empty dict mean no preferences
def set_user_preferences(uid, pref):
assert(type(pref) == type({}))
run_sql("UPDATE user SET settings=%s WHERE id=%s",
(serialize_via_marshal(pref), uid))
def get_default_user_preferences():
user_preference = {
'login_method': ''}
- for system in CFG_EXTERNAL_AUTHENTICATION.keys():
- if CFG_EXTERNAL_AUTHENTICATION[system][1]:
- user_preference['login_method'] = system
- break
+ if CFG_EXTERNAL_AUTH_DEFAULT in CFG_EXTERNAL_AUTHENTICATION:
+ user_preference['login_method'] = CFG_EXTERNAL_AUTH_DEFAULT
return user_preference
def get_preferred_user_language(req):
def _get_language_from_req_header(accept_language_header):
"""Extract langs info from req.headers_in['Accept-Language'] which
should be set to something similar to:
'fr,en-us;q=0.7,en;q=0.3'
"""
tmp_langs = {}
for lang in accept_language_header.split(','):
lang = lang.split(';q=')
if len(lang) == 2:
lang[1] = lang[1].replace('"', '') # Hack for Yeti robot
try:
tmp_langs[float(lang[1])] = lang[0]
except ValueError:
pass
else:
tmp_langs[1.0] = lang[0]
ret = []
priorities = tmp_langs.keys()
priorities.sort()
priorities.reverse()
for priority in priorities:
ret.append(tmp_langs[priority])
return ret
uid = getUid(req)
guest = isGuestUser(uid)
new_lang = None
preferred_lang = None
if not guest:
user_preferences = get_user_preferences(uid)
preferred_lang = new_lang = user_preferences.get('language', None)
if not new_lang:
try:
new_lang = wash_languages(cgi.parse_qs(req.args)['ln'])
except (TypeError, AttributeError, KeyError):
pass
if not new_lang:
try:
new_lang = wash_languages(_get_language_from_req_header(req.headers_in['Accept-Language']))
except (TypeError, AttributeError, KeyError):
pass
new_lang = wash_language(new_lang)
if new_lang != preferred_lang and not guest:
user_preferences['language'] = new_lang
set_user_preferences(uid, user_preferences)
return new_lang
def collect_user_info(req, login_time=False, refresh=False):
"""Given the mod_python request object rec or a uid it returns a dictionary
containing at least the keys uid, nickname, email, groups, plus any external keys in
the user preferences (collected at login time and built by the different
external authentication plugins) and if the mod_python request object is
provided, also the remote_ip, remote_host, referer, agent fields.
- If the user is authenticated with Apache should provide also
- apache_user and apache_group.
NOTE: if req is a mod_python request object, the user_info dictionary
is saved into req._user_info (for caching purpouses)
setApacheUser & setUid will properly reset it.
"""
from invenio.search_engine import get_permitted_restricted_collections
user_info = {
'remote_ip' : '',
'remote_host' : '',
'referer' : '',
'uri' : '',
'agent' : '',
- 'apache_user' : '',
- 'apache_group' : [],
'uid' : -1,
'nickname' : '',
'email' : '',
'group' : [],
'guest' : '1',
'session' : None,
'precached_permitted_restricted_collections' : [],
'precached_usebaskets' : False,
'precached_useloans' : False,
'precached_usegroups' : False,
'precached_usealerts' : False,
'precached_usemessages' : False,
'precached_viewsubmissions' : False,
'precached_useapprove' : False,
'precached_useadmin' : False,
'precached_usestats' : False,
}
try:
is_req = False
- if req is None:
+ if not req:
uid = -1
elif type(req) in (type(1), type(1L)):
## req is infact a user identification
uid = req
elif type(req) is dict:
## req is by mistake already a user_info
try:
assert(req.has_key('uid'))
assert(req.has_key('email'))
assert(req.has_key('nickname'))
except AssertionError:
## mmh... misuse of collect_user_info. Better warn the admin!
register_exception(alert_admin=True)
user_info.update(req)
return user_info
else:
is_req = True
uid = getUid(req)
if hasattr(req, '_user_info') and not login_time:
user_info = req._user_info
if not refresh:
return req._user_info
req._user_info = user_info
try:
user_info['remote_ip'] = req.remote_ip
except gaierror:
#FIXME: we should support IPV6 too. (hint for FireRole)
pass
user_info['session'] = get_session(req).sid()
user_info['remote_host'] = req.remote_host or ''
user_info['referer'] = req.headers_in.get('Referer', '')
user_info['uri'] = req.unparsed_uri or ()
user_info['agent'] = req.headers_in.get('User-Agent', 'N/A')
- try:
- user_info['apache_user'] = getApacheUser(req)
- if user_info['apache_user']:
- user_info['apache_group'] = auth_apache_user_in_groups(user_info['apache_user'])
- except AttributeError:
- pass
user_info['uid'] = uid
user_info['nickname'] = get_nickname(uid) or ''
user_info['email'] = get_email(uid) or ''
user_info['group'] = []
user_info['guest'] = str(isGuestUser(uid))
if user_info['guest'] == '0':
user_info['group'] = [group[1] for group in get_groups(uid)]
prefs = get_user_preferences(uid)
login_method = prefs['login_method']
- login_object = CFG_EXTERNAL_AUTHENTICATION[login_method][0]
+ login_object = CFG_EXTERNAL_AUTHENTICATION[login_method]
if login_object and ((datetime.datetime.now() - get_last_login(uid)).seconds > 3600):
## The user uses an external authentication method and it's a bit since
## she has not performed a login
if not CFG_EXTERNAL_AUTH_USING_SSO or (
is_req and req.is_https()):
## If we're using SSO we must be sure to be in HTTPS
## otherwise we can't really read anything, hence
## it's better skeep the synchronization
try:
groups = login_object.fetch_user_groups_membership(user_info['email'], req=req)
# groups is a dictionary {group_name : group_description,}
new_groups = {}
for key, value in groups.items():
new_groups[key + " [" + str(login_method) + "]"] = value
groups = new_groups
except (AttributeError, NotImplementedError, TypeError, InvenioWebAccessExternalAuthError):
pass
else: # Groups synchronization
from invenio.webgroup import synchronize_external_groups
synchronize_external_groups(uid, groups, login_method)
user_info['group'] = [group[1] for group in get_groups(uid)]
try:
# Importing external settings
new_prefs = login_object.fetch_user_preferences(user_info['email'], req=req)
for key, value in new_prefs.items():
prefs['EXTERNAL_' + key] = value
except (AttributeError, NotImplementedError, TypeError, InvenioWebAccessExternalAuthError):
pass
else:
set_user_preferences(uid, prefs)
prefs = get_user_preferences(uid)
run_sql('UPDATE user SET last_login=NOW() WHERE id=%s', (uid, ))
if prefs:
for key, value in prefs.iteritems():
user_info[key.lower()] = value
if login_time:
## Heavy computational information
from invenio.access_control_engine import acc_authorize_action
if CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL > 0:
user_info['precached_permitted_restricted_collections'] = get_permitted_restricted_collections(user_info)
user_info['precached_usebaskets'] = acc_authorize_action(user_info, 'usebaskets')[0] == 0
user_info['precached_useloans'] = acc_authorize_action(user_info, 'useloans')[0] == 0
user_info['precached_usegroups'] = acc_authorize_action(user_info, 'usegroups')[0] == 0
user_info['precached_usealerts'] = acc_authorize_action(user_info, 'usealerts')[0] == 0
user_info['precached_usemessages'] = acc_authorize_action(user_info, 'usemessages')[0] == 0
user_info['precached_usestats'] = acc_authorize_action(user_info, 'runwebstatadmin')[0] == 0
user_info['precached_viewsubmissions'] = isUserSubmitter(user_info)
user_info['precached_useapprove'] = isUserReferee(user_info)
user_info['precached_useadmin'] = isUserAdmin(user_info)
except Exception, e:
register_exception()
return user_info
-
-## --- follow some functions for Apache user/group authentication
-
-def _load_apache_password_file(apache_password_file=CFG_APACHE_PASSWORD_FILE):
- ret = {}
- for row in open(os.path.join(CFG_TMPDIR, apache_password_file)):
- row = row.split(':')
- if len(row) == 2:
- ret[row[0].strip()] = row[1].strip()
- return ret
-_apache_passwords = _load_apache_password_file()
-
-def auth_apache_user_p(user, password):
- """Check whether user-supplied credentials correspond to valid
- Apache password data file."""
- if user in _apache_passwords:
- password_apache = _apache_passwords[user]
- salt = password_apache[:2]
- return crypt.crypt(password, salt) == password_apache
- else:
- return False
-
-def _load_apache_group_file(apache_group_file=CFG_APACHE_GROUP_FILE):
- ret = {}
- for row in open(os.path.join(CFG_TMPDIR, apache_group_file)):
- row = row.split(':')
- if len(row) == 2:
- group = row[0].strip()
- users = row[1].strip().split(' ')
- for user in users:
- user = user.strip()
- if user not in ret:
- ret[user] = []
- ret[user].append(group)
- return ret
-_apache_groups = _load_apache_group_file()
-
-def auth_apache_user_in_groups(user):
- """Return list of Apache groups to which Apache user belong."""
- return _apache_groups.get(user, [])
-
-def http_get_credentials(req):
- if req.headers_in.has_key("Authorization"):
- try:
- s = req.headers_in["Authorization"][6:]
- s = base64.decodestring(s)
- user, passwd = s.split(":", 1)
- except (ValueError, base64.binascii.Error, base64.binascii.Incomplete):
- raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
- return (user, passwd)
- return (None, None)
-
-def http_check_credentials(req, role):
- """Retrieve Apache password and check user credential with the
- check_auth function. If this function returns True check if the user
- is enabled to the given role. If this is True, return, otherwise
- popup a new apache login box.
- """
-
- authorized = False
- while True:
- if req.headers_in.has_key("Authorization"):
- try:
- s = req.headers_in["Authorization"][6:]
- s = base64.decodestring(s)
- user, passwd = s.split(":", 1)
- except (ValueError, base64.binascii.Error, base64.binascii.Incomplete):
- raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
-
- authorized = auth_apache_user_p(user, passwd)
-
- if authorized:
- setApacheUser(req, user)
- authorized = acc_firerole_check_user(collect_user_info(req), load_role_definition(acc_get_role_id(role)))
- setApacheUser(req, '')
-
- if not authorized:
- # note that Opera supposedly doesn't like spaces around "=" below
- s = 'Basic realm="%s"' % role
- req.headers_out["WWW-Authenticate"] = s
- raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED
- else:
- setApacheUser(req, user)
- return
diff --git a/modules/websession/lib/webuser_tests.py b/modules/websession/lib/webuser_tests.py
index 939658dad..24525b0ce 100644
--- a/modules/websession/lib/webuser_tests.py
+++ b/modules/websession/lib/webuser_tests.py
@@ -1,87 +1,49 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Unit tests for the user handling library."""
__revision__ = "$Id$"
import unittest
from invenio import webuser
from invenio.testutils import make_test_suite, run_test_suite
from invenio.dbquery import run_sql
-class ApacheAuthenticationTests(unittest.TestCase):
- """Test functions related to the Apache authentication."""
-
- def test_auth_apache_user_p(self):
- """webuser - apache user password checking"""
- # These should succeed:
- self.assertEqual(True,
- webuser.auth_apache_user_p('jekyll', 'j123ekyll'))
- self.assertEqual(True,
- webuser.auth_apache_user_p('hyde', 'h123yde'))
- # Note: the following one should succeed even though the real
- # password is different, because crypt() looks at first 8
- # chars only:
- self.assertEqual(True,
- webuser.auth_apache_user_p('jekyll', 'j123ekylx'))
- # Now some attempts that should fail:
- self.assertEqual(False,
- webuser.auth_apache_user_p('jekyll', ''))
- self.assertEqual(False,
- webuser.auth_apache_user_p('jekyll', 'h123yde'))
- self.assertEqual(False,
- webuser.auth_apache_user_p('jekyll', 'aoeuidhtns'))
- self.assertEqual(False,
- webuser.auth_apache_user_p('aoeui', ''))
- self.assertEqual(False,
- webuser.auth_apache_user_p('aoeui', 'h123yde'))
- self.assertEqual(False,
- webuser.auth_apache_user_p('aoeui', 'dhtns'))
-
- def test_auth_apache_user_in_groups(self):
- """webuser - apache user group membership checking"""
- self.assertEqual(['theses'],
- webuser.auth_apache_user_in_groups('jekyll'))
- self.assertEqual([],
- webuser.auth_apache_user_in_groups('hyde'))
- self.assertEqual([],
- webuser.auth_apache_user_in_groups('aoeui'))
-
class IsUserSuperAdminTests(unittest.TestCase):
"""Test functions related to the isUserSuperAdmin function."""
def setUp(self):
self.id_admin = run_sql('SELECT id FROM user WHERE nickname="admin"')[0][0]
self.id_hyde = run_sql('SELECT id FROM user WHERE nickname="hyde"')[0][0]
def test_isUserSuperAdmin_admin(self):
"""webuser - isUserSuperAdmin with admin"""
self.failUnless(webuser.isUserSuperAdmin(webuser.collect_user_info(self.id_admin)))
def test_isUserSuperAdmin_hyde(self):
"""webuser - isUserSuperAdmin with hyde"""
self.failIf(webuser.isUserSuperAdmin(webuser.collect_user_info(self.id_hyde)))
-TEST_SUITE = make_test_suite(ApacheAuthenticationTests, IsUserSuperAdminTests)
+TEST_SUITE = make_test_suite(IsUserSuperAdminTests)
if __name__ == "__main__":
run_test_suite(TEST_SUITE)
diff --git a/modules/webstyle/img/Makefile.am b/modules/webstyle/img/Makefile.am
index 1c9436f79..338b6a852 100644
--- a/modules/webstyle/img/Makefile.am
+++ b/modules/webstyle/img/Makefile.am
@@ -1,237 +1,238 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
imgdir=$(localstatedir)/www/img
img_DATA = add-small.png \
add.png \
answer_bad.gif \
arrow_down.gif \
arrow_down2.png \
arrow_link-icon-15x11-right.gif \
arrow_up.gif \
arrow_up2.png \
at.gif \
balloon_arrow_left_shadow.png \
balloon_bottom_left_shadow.png \
balloon_bottom_shadow.png \
balloon_right_shadow.png \
balloon_top_right_shadow.png \
balloon_arrow_shadow.png \
balloon_bottom_right_shadow.png \
balloon_left_shadow.png \
balloon_top_left_shadow.png \
balloon_top_shadow.png \
blankicon.gif \
blue_gradient.gif \
bullet_toggle_minus.png \
bullet_toggle_plus.png \
circle_green.png \
circle_red.png \
cross_red.gif \
delete-big.png \
delete-small.png \
delete.png \
description.gif \
diff.png \
dot.gif \
down-trans.gif \
down.gif \
drop_down_menu_arrow_down_g.gif \
drop_down_menu_arrow_down_b.gif \
drop_down_menu_arrow_down_lb.gif \
drop_down_menu_arrow_down_w.gif \
edit1.gif \
external-icon-light-8x8.gif \
feed-icon-12x12.gif \
file-icon-none-96x128.gif \
file-icon-text-12x16.gif \
file-icon-text-15x20.gif \
file-icon-text-34x48.gif \
file-icon-text-96x128.gif \
forbidden_left.gif \
forbidden_right.gif \
gradient-lightgray-1x100.gif \
gradient_tab-gray-1x23.gif \
gradient_tab_on-gray-1x23.gif \
group_admin.png \
head.gif \
header_background.gif \
help.png \
icon-journal_Athanasius_Kircher_Atlantis.gif \
icon-journal_hms_beagle.gif \
iconcross.gif \
iconcross2.gif \
+ pix.png \
plus_orange.png \
iconeye.gif \
iconpen.gif \
indicator.gif \
journal-template1.gif \
journal-template2.gif \
journal-template3.gif \
journal-template4.gif \
journal_Athanasius_Kircher_Atlantis.gif \
journal_content.png \
journal_footer.png \
journal_footer2.png \
journal_header.png \
journal_hms_beagle.gif \
journal_new.png \
journal_virgin_forest.gif \
journal_water_dog.gif \
keep_sso_connection_alive.gif \
last-right-part-trans.gif \
left-part-topless-trans.gif \
left-part-trans.gif \
left-trans.gif \
left.gif \
line-up-trans.gif \
line.gif \
loading.gif \
mail-icon-12x8.gif \
mainmenu.gif \
merge-small.png \
merge.png \
mergeNC.png \
move.png \
move_from.gif \
move_to.gif \
noway.gif \
okay.gif \
out.gif \
paper-texture-128x128.gif \
paper_clip-72x72.gif \
r.gif \
rcorners-gray-1280x18.gif \
rcorners-gray-1280x60-folded.gif \
red_gradient.gif \
refresh.png \
replace.png \
restricted.gif \
right-part-topless-trans.gif \
right-part-trans.gif \
right-trans.gif \
right.gif \
rss.png \
sb.gif \
sbm_guide_accessnumber.png \
sbm_guide_approvals.png \
sbm_guide_approve_button.png \
sbm_guide_browse.png \
sbm_guide_description.png \
sbm_guide_login.png \
sbm_guide_logout.png \
sbm_guide_modify_button.png \
sbm_guide_revise_button.png \
sbm_guide_submissions.png \
sbm_guide_submit_button.png \
sbm_guide_subnumber.png \
sbm_guide_summary.png \
sclose.gif \
se.gif \
search.png \
site_logo.gif \
site_logo_rss.png \
site_logo_small.gif \
smallbin.gif \
smalldown.gif \
smallfiles.gif \
smallup.gif \
smchk_gr.gif \
smchk_rd.gif \
sn.gif \
sp.gif \
star-icon-30x30.gif \
star_dot-icon-30x30.gif \
star_empty-icon-30x30.gif \
star_half-icon-30x30.gif \
stars-0-0.png \
stars-0-5.png \
stars-1-0.png \
stars-1-5.png \
stars-2-0.png \
stars-2-5.png \
stars-3-0.png \
stars-3-5.png \
stars-4-0.png \
stars-4-5.png \
stars-5-0.png \
summary.gif \
table.png \
table_add.png \
table_delete.png \
table_multiple.png \
table_row_delete.png \
table_row_insert.png \
table_sort.png \
tick.gif \
tree_branch.gif \
up.gif \
user-icon-1-16x16.gif \
user-icon-1-20x20.gif \
user-icon-1-24x24.gif \
waiting_or.gif \
warning.png \
webbasket_create.png \
webbasket_create_small.png \
webbasket_delete.png \
webbasket_down.png \
webbasket_extern.png \
webbasket_intern.png \
webbasket_move.png \
webbasket_ugs.png \
webbasket_up.png \
webbasket_us.png \
webbasket_user.png \
webbasket_usergroup.png \
webbasket_usergroup_gray.png \
webbasket_world.png \
webbasket_ws.png \
wb-add-note.png \
wb-copy-item.png \
wb-create-basket.png \
wb-delete-basket.png \
wb-delete-item.png \
wb-edit-basket.png \
wb-edit-topic.png \
wb-external-item.png \
wb-go-back.png \
wb-move-item-down-disabled.png \
wb-move-item-down.png \
wb-move-item-up-disabled.png \
wb-move-item-up.png \
wb-next-item-disabled.png \
wb-next-item.png \
wb-notes.png \
wb-previous-item-disabled.png \
wb-previous-item.png \
wb-sort-asc.gif \
wb-sort-desc.gif \
wb-sort-none.gif \
wb-subscribe.png \
wb-unsubscribe.png \
white_field.gif \
wsignout.gif \
compare.png
tmpdir=$(localstatedir)/tmp
tmp_DATA = plotextractor_dummy.png
EXTRA_DIST = $(img_DATA) $(tmp_DATA)
CLEANFILES = *~ *.tmp
diff --git a/modules/webstyle/img/pix.png b/modules/webstyle/img/pix.png
new file mode 100644
index 000000000..0da93fd36
Binary files /dev/null and b/modules/webstyle/img/pix.png differ
diff --git a/modules/webstyle/lib/httptest_webinterface.py b/modules/webstyle/lib/httptest_webinterface.py
index 4943a6184..96b453ad3 100644
--- a/modules/webstyle/lib/httptest_webinterface.py
+++ b/modules/webstyle/lib/httptest_webinterface.py
@@ -1,91 +1,98 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
HTTP Test web interface. This is the place where to put helpers for
regression tests related to HTTP (or WSGI or SSO).
"""
__revision__ = \
"$Id$"
__lastupdated__ = """$Date$"""
import cgi
from invenio.config import CFG_SITE_URL
from invenio.webpage import page
from invenio.webinterface_handler import WebInterfaceDirectory
from invenio.urlutils import redirect_to_url
class WebInterfaceHTTPTestPages(WebInterfaceDirectory):
- _exports = ["", "post1", "post2", "sso", "dumpreq"]
+ _exports = ["", "post1", "post2", "sso", "dumpreq", "whatismyip"]
def __call__(self, req, form):
redirect_to_url(req, CFG_SITE_URL + '/httptest/post1')
index = __call__
def sso(self, req, form):
""" For testing single sign-on """
req.add_common_vars()
sso_env = {}
for var, value in req.subprocess_env.iteritems():
if var.startswith('HTTP_ADFS_'):
sso_env[var] = value
out = "
SSO test"
out += "
"
for var, value in sso_env.iteritems():
out += "
%s
%s
" % (var, value)
out += "
"
return out
def dumpreq(self, req, form):
"""
Dump a textual representation of the request object.
"""
return "
%s
" % cgi.escape(str(req))
def post1(self, req, form):
"""
This is used by WSGI regression test, to test if it's possible
to upload a file and retrieve it correctly.
"""
if req.method == 'POST':
if 'file' in form:
for row in form['file'].file:
req.write(row)
return ''
else:
body = """
"""
return page("test1", body=body, req=req)
def post2(self, req, form):
"""
This is to test L{handle_file_post} function.
"""
from invenio.webinterface_handler_wsgi_utils import handle_file_post
from invenio.bibdocfile import stream_file
if req.method != 'POST':
body = """
Please send a file via POST.
"""
return page("test2", body=body, req=req)
path, mimetype = handle_file_post(req)
- return stream_file(req, path, mime=mimetype)
+ return stream_file(req, path, mime=mimetype)
+
+ def whatismyip(self, req, form):
+ """
+ Return the client IP as seen by the server (useful for testing e.g. Robot authentication)
+ """
+ req.content_type = "text/plain"
+ return req.remote_ip
diff --git a/modules/websubmit/lib/websubmit_webinterface.py b/modules/websubmit/lib/websubmit_webinterface.py
index ede41ac88..dce9b379c 100644
--- a/modules/websubmit/lib/websubmit_webinterface.py
+++ b/modules/websubmit/lib/websubmit_webinterface.py
@@ -1,1176 +1,1175 @@
## This file is part of Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__lastupdated__ = """$Date$"""
__revision__ = "$Id$"
import os
import time
import cgi
import sys
import shutil
from urllib import urlencode
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_TMPDIR, \
CFG_SITE_NAME_INTL, \
CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_WEBSUBMIT_STORAGEDIR, \
CFG_PREFIX
from invenio import webinterface_handler_config as apache
from invenio.dbquery import run_sql
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_admin import acc_is_role
from invenio.webpage import page, create_error_box, pageheaderonly, \
pagefooteronly
from invenio.webuser import getUid, page_not_authorized, collect_user_info, isGuestUser, isUserSuperAdmin
from invenio.websubmit_config import *
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import make_canonical_urlargd, redirect_to_url
from invenio.messages import gettext_set_language
from invenio.search_engine import \
guess_primary_collection_of_a_record, get_colID, record_exists, \
create_navtrail_links, check_user_can_view_record, record_empty
from invenio.bibdocfile import BibRecDocs, normalize_format, file_strip_ext, \
stream_restricted_icon, BibDoc, InvenioWebSubmitFileError, stream_file, \
decompose_file, propose_next_docname, get_subformat_from_format
from invenio.errorlib import register_exception
from invenio.websubmit_icon_creator import create_icon, InvenioWebSubmitIconCreatorError
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.session import get_session
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
try:
from invenio.fckeditor_invenio_connector import FCKeditorConnectorInvenio
fckeditor_available = True
except ImportError, e:
fckeditor_available = False
from invenio.websubmit_managedocfiles import \
create_file_upload_interface, \
get_upload_file_interface_javascript, \
get_upload_file_interface_css, \
move_uploaded_files_to_storage
class WebInterfaceFilesPages(WebInterfaceDirectory):
def __init__(self,recid):
self.recid = recid
def _lookup(self, component, path):
# after /record//files/ every part is used as the file
# name
filename = component
def getfile(req, form):
args = wash_urlargd(form, websubmit_templates.files_default_urlargd)
ln = args['ln']
_ = gettext_set_language(ln)
uid = getUid(req)
user_info = collect_user_info(req)
verbose = args['verbose']
if verbose >= 1 and not isUserSuperAdmin(user_info):
# Only SuperUser can see all the details!
verbose = 0
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE > 1:
return page_not_authorized(req, "/record/%s" % self.recid,
navmenuid='submit')
if record_exists(self.recid) < 1:
msg = "
%s
" % _("Requested record does not seem to exist.")
return warningMsg(msg, req, CFG_SITE_NAME, ln)
if record_empty(self.recid):
msg = "
%s
" % _("Requested record does not seem to have been integrated.")
return warningMsg(msg, req, CFG_SITE_NAME, ln)
(auth_code, auth_message) = check_user_can_view_record(user_info, self.recid)
- if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : ln, 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_message)
readonly = CFG_ACCESS_CONTROL_LEVEL_SITE == 1
# From now on: either the user provided a specific file
# name (and a possible version), or we return a list of
# all the available files. In no case are the docids
# visible.
try:
bibarchive = BibRecDocs(self.recid)
except InvenioWebSubmitFileError, e:
register_exception(req=req, alert_admin=True)
msg = "
%s
%s
" % (
_("The system has encountered an error in retrieving the list of files for this document."),
_("The error has been logged and will be taken in consideration as soon as possible."))
return warningMsg(msg, req, CFG_SITE_NAME, ln)
if bibarchive.deleted_p():
return print_warning(req, _("Requested record does not seem to exist."))
docname = ''
format = ''
version = ''
warn = ''
if filename:
# We know the complete file name, guess which docid it
# refers to
## TODO: Change the extension system according to ext.py from setlink
## and have a uniform extension mechanism...
docname = file_strip_ext(filename)
format = filename[len(docname):]
if format and format[0] != '.':
format = '.' + format
if args['subformat']:
format += ';%s' % args['subformat']
else:
docname = args['docname']
if not format:
format = args['format']
if args['subformat']:
format += ';%s' % args['subformat']
if not version:
version = args['version']
# version could be either empty, or all or an integer
try:
int(version)
except ValueError:
if version != 'all':
version = ''
display_hidden = isUserSuperAdmin(user_info)
if version != 'all':
# search this filename in the complete list of files
for doc in bibarchive.list_bibdocs():
if docname == doc.get_docname():
try:
docfile = doc.get_file(format, version)
(auth_code, auth_message) = docfile.is_restricted(user_info)
if auth_code != 0:
if CFG_WEBSUBMIT_ICON_SUBFORMAT_RE.match(get_subformat_from_format(format)):
return stream_restricted_icon(req)
- if user_info['email'] == 'guest' and not user_info['apache_user']:
+ if user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action('viewrestrdoc', {'status' : docfile.get_status()})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : ln, 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
redirect_to_url(req, target)
else:
req.status = apache.HTTP_UNAUTHORIZED
warn += print_warning(_("This file is restricted: ") + auth_message)
break
if not docfile.hidden_p():
if not readonly:
ip = str(req.remote_ip)
res = doc.register_download(ip, version, format, uid)
try:
return docfile.stream(req)
except InvenioWebSubmitFileError, msg:
register_exception(req=req, alert_admin=True)
req.status = apache.HTTP_INTERNAL_SERVER_ERROR
return warningMsg(_("An error has happened in trying to stream the request file."), req, CFG_SITE_NAME, ln)
else:
req.status = apache.HTTP_UNAUTHORIZED
warn = print_warning(_("The requested file is hidden and can not be accessed."))
except InvenioWebSubmitFileError, msg:
register_exception(req=req, alert_admin=True)
if docname and format and not warn:
req.status = apache.HTTP_NOT_FOUND
warn += print_warning(_("Requested file does not seem to exist."))
filelist = bibarchive.display("", version, ln=ln, verbose=verbose, display_hidden=display_hidden)
t = warn + websubmit_templates.tmpl_filelist(
ln=ln,
recid=self.recid,
docname=args['docname'],
version=version,
filelist=filelist)
cc = guess_primary_collection_of_a_record(self.recid)
unordered_tabs = get_detailed_page_tabs(get_colID(cc), self.recid, ln)
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x,y: cmp(x[1],y[1]))
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
tabs = [(unordered_tabs[tab_id]['label'], \
'%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id == 'files',
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
args['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
args['ln'])
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, self.recid, args['ln'])
return pageheaderonly(title=title,
navtrail=create_navtrail_links(cc=cc, aas=0, ln=ln) + \
''' > %s
> %s''' % \
(CFG_SITE_URL, self.recid, title, _("Access to Fulltext")),
description="",
keywords="keywords",
uid=uid,
language=ln,
req=req,
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(ln) + \
top + t + bottom + \
websearch_templates.tmpl_search_pageend(ln) + \
pagefooteronly(lastupdated=__lastupdated__, language=ln, req=req)
return getfile, []
def __call__(self, req, form):
"""Called in case of URLs like /record/123/files without
trailing slash.
"""
args = wash_urlargd(form, websubmit_templates.files_default_urlargd)
ln = args['ln']
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
return redirect_to_url(req, '%s/record/%s/files/%s' % (CFG_SITE_URL, self.recid, link_ln))
def websubmit_legacy_getfile(req, form):
""" Handle legacy /getfile.py URLs """
args = wash_urlargd(form, {
'recid': (int, 0),
'docid': (int, 0),
'version': (str, ''),
'name': (str, ''),
'format': (str, ''),
'ln' : (str, CFG_SITE_LANG)
})
_ = gettext_set_language(args['ln'])
def _getfile_py(req, recid=0, docid=0, version="", name="", format="", ln=CFG_SITE_LANG):
if not recid:
## Let's obtain the recid from the docid
if docid:
try:
bibdoc = BibDoc(docid=docid)
recid = bibdoc.get_recid()
except InvenioWebSubmitFileError, e:
return warningMsg(_("An error has happened in trying to retrieve the requested file."), req, CFG_SITE_NAME, ln)
else:
return warningMsg(_('Not enough information to retrieve the document'), req, CFG_SITE_NAME, ln)
else:
if not name and docid:
## Let's obtain the name from the docid
try:
bibdoc = BibDoc(docid)
name = bibdoc.get_docname()
except InvenioWebSubmitFileError, e:
return warningMsg(_("An error has happened in trying to retrieving the requested file."), req, CFG_SITE_NAME, ln)
format = normalize_format(format)
redirect_to_url(req, '%s/record/%s/files/%s%s?ln=%s%s' % (CFG_SITE_URL, recid, name, format, ln, version and 'version=%s' % version or ''), apache.HTTP_MOVED_PERMANENTLY)
return _getfile_py(req, **args)
# --------------------------------------------------
from invenio.websubmit_engine import home, action, interface, endaction
class WebInterfaceSubmitPages(WebInterfaceDirectory):
_exports = ['summary', 'sub', 'direct', '', 'attachfile', 'uploadfile', \
'getuploadedfile', 'managedocfiles', 'managedocfilesasync']
def managedocfiles(self, req, form):
"""
Display admin interface to manage files of a record
"""
argd = wash_urlargd(form, {
'ln': (str, ''),
'access': (str, ''),
'recid': (int, None),
'do': (int, 0),
'cancel': (str, None),
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
user_info = collect_user_info(req)
# Check authorization
(auth_code, auth_msg) = acc_authorize_action(req,
'runbibdocfile')
- if auth_code and user_info['email'] == 'guest' and \
- not user_info['apache_user']:
+ if auth_code and user_info['email'] == 'guest':
# Ask to login
target = '/youraccount/login' + \
make_canonical_urlargd({'ln' : argd['ln'],
'referer' : CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, referer="/submit/managedocfiles",
uid=uid, text=auth_msg,
ln=argd['ln'],
navmenuid="admin")
# Prepare navtrail
navtrail = '''Admin Area > %(manage_files)s''' \
% {'CFG_SITE_URL': CFG_SITE_URL,
'manage_files': _("Manage Document Files")}
body = ''
if argd['do'] != 0 and not argd['cancel']:
# Apply modifications
working_dir = os.path.join(CFG_TMPDIR,
'websubmit_upload_interface_config_' + str(uid),
argd['access'])
move_uploaded_files_to_storage(working_dir=working_dir,
recid=argd['recid'],
icon_sizes=['180>','700>'],
create_icon_doctypes=['*'],
force_file_revision=False)
# Clean temporary directory
shutil.rmtree(working_dir)
# Confirm modifications
body += '
%s
' % \
(_('Your modifications to record #%i have been submitted') % argd['recid'])
elif argd['cancel']:
# Clean temporary directory
working_dir = os.path.join(CFG_TMPDIR,
'websubmit_upload_interface_config_' + str(uid),
argd['access'])
shutil.rmtree(working_dir)
body += '
%s
' % \
(_('Your modifications to record #%i have been cancelled') % argd['recid'])
if not argd['recid'] or argd['do'] != 0:
body += '''
''' % {'edit': _('Edit'),
'edit_record': _('Edit record'),
'CFG_SITE_URL': CFG_SITE_URL}
access = time.strftime('%Y%m%d_%H%M%S')
if argd['recid'] and argd['do'] == 0:
# Displaying interface to manage files
# Prepare navtrail
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, argd['recid'],
argd['ln'])
navtrail = '''Admin Area >
%(manage_files)s >
%(record)s: %(title)s
''' \
% {'CFG_SITE_URL': CFG_SITE_URL,
'title': title,
'manage_files': _("Document File Manager"),
'record': _("Record #%i") % argd['recid']}
# FIXME: add parameters to `runbibdocfile' in order to
# configure the file editor based on role, or at least
# move configuration below to some config file.
body += create_file_upload_interface(\
recid=argd['recid'],
ln=argd['ln'],
doctypes_and_desc=[('main', 'Main document'),
('latex', 'LaTeX'),
('source', 'Source'),
('additional', 'Additional File'),
('audio', 'Audio file'),
('video', 'Video file'),
('script', 'Script'),
('data', 'Data'),
('figure', 'Figure'),
('schema', 'Schema'),
('graph', 'Graph'),
('image', 'Image'),
('drawing', 'Drawing'),
('slides', 'Slides')],
can_revise_doctypes=['*'],
can_comment_doctypes=['*'],
can_describe_doctypes=['*'],
can_delete_doctypes=['*'],
can_keep_doctypes=['*'],
can_rename_doctypes=['*'],
can_add_format_to_doctypes=['*'],
can_restrict_doctypes=['*'],
restrictions_and_desc=[('', 'Public'),
('restricted', 'Restricted')],
uid=uid,
sbm_access=access)[1]
body += '''
''' % \
{'apply_changes': _("Apply changes"),
'cancel_changes': _("Cancel all changes"),
'recid': argd['recid'],
'access': access,
'ln': argd['ln'],
'CFG_SITE_URL': CFG_SITE_URL}
body += websubmit_templates.tmpl_page_do_not_leave_submission_js(argd['ln'], enabled=True)
return page(title = _("Document File Manager") + (argd['recid'] and (': ' + _("Record #%i") % argd['recid']) or ''),
navtrail=navtrail,
navtrail_append_title_p=0,
metaheaderadd = get_upload_file_interface_javascript(form_url_params='?access='+access) + \
get_upload_file_interface_css(),
body = body,
uid = uid,
language=argd['ln'],
req=req,
navmenuid='admin')
def managedocfilesasync(self, req, form):
"Upload file and returns upload interface"
argd = wash_urlargd(form, {
'ln': (str, ''),
'recid': (int, 1),
'doctype': (str, ''),
'access': (str, ''),
'indir': (str, ''),
})
user_info = collect_user_info(req)
include_headers = False
# User submitted either through WebSubmit, or admin interface.
if form.has_key('doctype') and form.has_key('indir') \
and form.has_key('access'):
# Submitted through WebSubmit. Check rights
include_headers = True
working_dir = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'], argd['doctype'],
argd['access'])
try:
assert(working_dir == os.path.abspath(working_dir))
except AssertionError:
return apache.HTTP_UNAUTHORIZED
try:
# Retrieve recid from working_dir, safer.
recid_fd = file(os.path.join(working_dir, 'SN'))
recid = int(recid_fd.read())
recid_fd.close()
except:
recid = ""
try:
act_fd = file(os.path.join(working_dir, 'act'))
action = act_fd.read()
act_fd.close()
except:
action = ""
# Is user authorized to perform this action?
(auth_code, auth_msg) = acc_authorize_action(user_info,
"submit",
doctype=argd['doctype'],
act=action)
else:
# User must be allowed to attach files
(auth_code, auth_msg) = acc_authorize_action(user_info,
'runbibdocfile')
recid = argd['recid']
if auth_code:
return apache.HTTP_UNAUTHORIZED
return create_file_upload_interface(recid=recid,
ln=argd['ln'],
print_outside_form_tag=False,
print_envelope=False,
form=form,
include_headers=include_headers,
sbm_indir=argd['indir'],
sbm_access=argd['access'],
sbm_doctype=argd['doctype'],
uid=user_info['uid'])[1]
def uploadfile(self, req, form):
"""
Similar to /submit, but only consider files. Nice for
asynchronous Javascript uploads. Should be used to upload a
single file.
Also try to create an icon, and return URL to file(s) + icon(s)
Authentication is performed based on session ID passed as
parameter instead of cookie-based authentication, due to the
use of this URL by the Flash plugin (to upload multiple files
at once), which does not route cookies.
FIXME: consider adding /deletefile and /modifyfile functions +
parsing of additional parameters to rename files, add
comments, restrictions, etc.
"""
if sys.hexversion < 0x2060000:
try:
import simplejson as json
simplejson_available = True
except ImportError:
# Okay, no Ajax app will be possible, but continue anyway,
# since this package is only recommended, not mandatory.
simplejson_available = False
else:
import json
simplejson_available = True
argd = wash_urlargd(form, {
'doctype': (str, ''),
'access': (str, ''),
'indir': (str, ''),
'session_id': (str, ''),
'rename': (str, ''),
})
curdir = None
if not form.has_key("indir") or \
not form.has_key("doctype") or \
not form.has_key("access"):
return apache.HTTP_BAD_REQUEST
else:
curdir = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'],
argd['doctype'],
argd['access'])
user_info = collect_user_info(req)
if form.has_key("session_id"):
# Are we uploading using Flash, which does not transmit
# cookie? The expect to receive session_id as a form
# parameter. First check that IP addresses do not
# mismatch. A ValueError will be raises if there is
# something wrong
session = get_session(req=req, sid=argd['session_id'])
try:
session = get_session(req=req, sid=argd['session_id'])
except ValueError, e:
return apache.HTTP_BAD_REQUEST
# Retrieve user information. We cannot rely on the session here.
res = run_sql("SELECT uid FROM session WHERE session_key=%s", (argd['session_id'],))
if len(res):
uid = res[0][0]
user_info = collect_user_info(uid)
try:
act_fd = file(os.path.join(curdir, 'act'))
action = act_fd.read()
act_fd.close()
except:
act = ""
# Is user authorized to perform this action?
(auth_code, auth_message) = acc_authorize_action(uid, "submit",
verbose=0,
doctype=argd['doctype'],
act=action)
if acc_is_role("submit", doctype=argd['doctype'], act=action) and auth_code != 0:
# User cannot submit
return apache.HTTP_UNAUTHORIZED
else:
# Process the upload and get the response
added_files = {}
for key, formfields in form.items():
filename = key.replace("[]", "")
file_to_open = os.path.join(curdir, filename)
if hasattr(formfields, "filename") and formfields.filename:
dir_to_open = os.path.abspath(os.path.join(curdir,
'files',
str(user_info['uid']),
key))
try:
assert(dir_to_open.startswith(CFG_WEBSUBMIT_STORAGEDIR))
except AssertionError:
register_exception(req=req, prefix='curdir="%s", key="%s"' % (curdir, key))
return apache.HTTP_FORBIDDEN
if not os.path.exists(dir_to_open):
try:
os.makedirs(dir_to_open)
except:
register_exception(req=req, alert_admin=True)
return apache.HTTP_FORBIDDEN
filename = formfields.filename
## Before saving the file to disc, wash the filename (in particular
## washing away UNIX and Windows (e.g. DFS) paths):
filename = os.path.basename(filename.split('\\')[-1])
filename = filename.strip()
if filename != "":
# Check that file does not already exist
n = 1
while os.path.exists(os.path.join(dir_to_open, filename)):
#dirname, basename, extension = decompose_file(new_destination_path)
basedir, name, extension = decompose_file(filename)
new_name = propose_next_docname(name)
filename = new_name + extension
# This may be dangerous if the file size is bigger than the available memory
fp = open(os.path.join(dir_to_open, filename), "w")
fp.write(formfields.file.read())
fp.close()
fp = open(os.path.join(curdir, "lastuploadedfile"), "w")
fp.write(filename)
fp.close()
fp = open(file_to_open, "w")
fp.write(filename)
fp.close()
try:
# Create icon
(icon_path, icon_name) = create_icon(
{ 'input-file' : os.path.join(dir_to_open, filename),
'icon-name' : filename, # extension stripped automatically
'icon-file-format' : 'gif',
'multipage-icon' : False,
'multipage-icon-delay' : 100,
'icon-scale' : "300>", # Resize only if width > 300
'verbosity' : 0,
})
icons_dir = os.path.join(os.path.join(curdir,
'icons',
str(user_info['uid']),
key))
if not os.path.exists(icons_dir):
# Create uid/icons dir if needed
os.makedirs(icons_dir)
os.rename(os.path.join(icon_path, icon_name),
os.path.join(icons_dir, icon_name))
added_files[key] = {'name': filename,
'iconName': icon_name}
except InvenioWebSubmitIconCreatorError, e:
# We could not create the icon
added_files[key] = {'name': filename}
continue
else:
return apache.HTTP_BAD_REQUEST
# Send our response
if simplejson_available:
return json.dumps(added_files)
def getuploadedfile(self, req, form):
"""
Stream uploaded files.
For the moment, restrict to files in ./curdir/files/uid or
./curdir/icons/uid directory, so that we are sure we stream
files only to the user who uploaded them.
"""
argd = wash_urlargd(form, {'indir': (str, None),
'doctype': (str, None),
'access': (str, None),
'icon': (int, 0),
'key': (str, None),
'filename': (str, None)})
if None in argd.values():
return apache.HTTP_BAD_REQUEST
uid = getUid(req)
if argd['icon']:
file_path = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'],
argd['doctype'],
argd['access'],
'icons',
str(uid),
argd['key'],
argd['filename']
)
else:
file_path = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'],
argd['doctype'],
argd['access'],
'files',
str(uid),
argd['key'],
argd['filename']
)
abs_file_path = os.path.abspath(file_path)
if abs_file_path.startswith(CFG_WEBSUBMIT_STORAGEDIR):
# Check if file exist. Note that icon might not yet have
# been created.
for i in range(5):
if os.path.exists(abs_file_path):
return stream_file(req, abs_file_path)
time.sleep(1)
# Send error 404 in all other cases
return apache.HTTP_NOT_FOUND
def attachfile(self, req, form):
"""
Process requests received from FCKeditor to upload files.
If the uploaded file is an image, create an icon version
"""
if not fckeditor_available:
return apache.HTTP_NOT_FOUND
if not form.has_key('type'):
form['type'] = 'File'
if not form.has_key('NewFile') or \
not form['type'] in \
['File', 'Image', 'Flash', 'Media']:
return apache.HTTP_NOT_FOUND
uid = getUid(req)
# URL where the file can be fetched after upload
user_files_path = '%(CFG_SITE_URL)s/submit/getattachedfile/%(uid)s' % \
{'uid': uid,
'CFG_SITE_URL': CFG_SITE_URL}
# Path to directory where uploaded files are saved
user_files_absolute_path = '%(CFG_PREFIX)s/var/tmp/attachfile/%(uid)s' % \
{'uid': uid,
'CFG_PREFIX': CFG_PREFIX}
try:
os.makedirs(user_files_absolute_path)
except:
pass
# Create a Connector instance to handle the request
conn = FCKeditorConnectorInvenio(form, recid=-1, uid=uid,
allowed_commands=['QuickUpload'],
allowed_types = ['File', 'Image', 'Flash', 'Media'],
user_files_path = user_files_path,
user_files_absolute_path = user_files_absolute_path)
user_info = collect_user_info(req)
(auth_code, auth_message) = acc_authorize_action(user_info, 'attachsubmissionfile')
- if user_info['email'] == 'guest' and not user_info['apache_user']:
+ if user_info['email'] == 'guest':
# User is guest: must login prior to upload
data = conn.sendUploadResults(1, '', '', 'Please login before uploading file.')
elif auth_code:
# User cannot submit
data = conn.sendUploadResults(1, '', '', 'Sorry, you are not allowed to submit files.')
else:
# Process the upload and get the response
data = conn.doResponse()
# At this point, the file has been uploaded. The FCKeditor
# submit the image in form['NewFile']. However, the image
# might have been renamed in between by the FCK connector on
# the server side, by appending (%04d) at the end of the base
# name. Retrieve that file
uploaded_file_path = os.path.join(user_files_absolute_path,
form['type'].lower(),
form['NewFile'].filename)
uploaded_file_path = retrieve_most_recent_attached_file(uploaded_file_path)
uploaded_file_name = os.path.basename(uploaded_file_path)
# Create an icon
if form.get('type','') == 'Image':
try:
(icon_path, icon_name) = create_icon(
{ 'input-file' : uploaded_file_path,
'icon-name' : os.path.splitext(uploaded_file_name)[0],
'icon-file-format' : os.path.splitext(uploaded_file_name)[1][1:] or 'gif',
'multipage-icon' : False,
'multipage-icon-delay' : 100,
'icon-scale' : "300>", # Resize only if width > 300
'verbosity' : 0,
})
# Move original file to /original dir, and replace it with icon file
original_user_files_absolute_path = os.path.join(user_files_absolute_path,
'image', 'original')
if not os.path.exists(original_user_files_absolute_path):
# Create /original dir if needed
os.mkdir(original_user_files_absolute_path)
os.rename(uploaded_file_path,
original_user_files_absolute_path + os.sep + uploaded_file_name)
os.rename(icon_path + os.sep + icon_name,
uploaded_file_path)
except InvenioWebSubmitIconCreatorError, e:
pass
# Transform the headers into something ok for mod_python
for header in conn.headers:
if not header is None:
if header[0] == 'Content-Type':
req.content_type = header[1]
else:
req.headers_out[header[0]] = header[1]
# Send our response
req.send_http_header()
req.write(data)
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for getting
and putting attachments) Eg:
/submit/getattachedfile/41336978/image/myfigure.png
/submit/attachfile/41336978/image/myfigure.png
"""
if component == 'getattachedfile' and len(path) > 2:
uid = path[0] # uid of the submitter
file_type = path[1] # file, image, flash or media (as
# defined by FCKeditor)
if file_type in ['file', 'image', 'flash', 'media']:
file_name = '/'.join(path[2:]) # the filename
def answer_get(req, form):
"""Accessing files attached to submission."""
form['file'] = file_name
form['type'] = file_type
form['uid'] = uid
return self.getattachedfile(req, form)
return answer_get, []
# All other cases: file not found
return None, []
def getattachedfile(self, req, form):
"""
Returns a file uploaded to the submission 'drop box' by the
FCKeditor.
"""
argd = wash_urlargd(form, {'file': (str, None),
'type': (str, None),
'uid': (int, 0)})
# Can user view this record, i.e. can user access its
# attachments?
uid = getUid(req)
user_info = collect_user_info(req)
if not argd['file'] is None:
# Prepare path to file on disk. Normalize the path so that
# ../ and other dangerous components are removed.
path = os.path.abspath(CFG_PREFIX + '/var/tmp/attachfile/' + \
'/' + str(argd['uid']) + \
'/' + argd['type'] + '/' + argd['file'])
# Check that we are really accessing attachements
# directory, for the declared record.
if path.startswith(CFG_PREFIX + '/var/tmp/attachfile/') and os.path.exists(path):
return stream_file(req, path)
# Send error 404 in all other cases
return(apache.HTTP_NOT_FOUND)
def direct(self, req, form):
"""Directly redirected to an initialized submission."""
args = wash_urlargd(form, {'sub': (str, ''),
'access' : (str, '')})
sub = args['sub']
access = args['access']
ln = args['ln']
_ = gettext_set_language(ln)
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "direct",
navmenuid='submit')
myQuery = req.args
if not sub:
return warningMsg(_("Sorry, 'sub' parameter missing..."), req, ln=ln)
res = run_sql("SELECT docname,actname FROM sbmIMPLEMENT WHERE subname=%s", (sub,))
if not res:
return warningMsg(_("Sorry. Cannot analyse parameter"), req, ln=ln)
else:
# get document type
doctype = res[0][0]
# get action name
action = res[0][1]
# retrieve other parameter values
params = dict(form)
# find existing access number
if not access:
# create 'unique' access number
pid = os.getpid()
now = time.time()
access = "%i_%s" % (now,pid)
# retrieve 'dir' value
res = run_sql ("SELECT dir FROM sbmACTION WHERE sactname=%s", (action,))
dir = res[0][0]
mainmenu = req.headers_in.get('referer')
params['access'] = access
params['act'] = action
params['doctype'] = doctype
params['startPg'] = '1'
params['mainmenu'] = mainmenu
params['ln'] = ln
params['indir'] = dir
url = "%s/submit?%s" % (CFG_SITE_URL, urlencode(params))
redirect_to_url(req, url)
def sub(self, req, form):
"""DEPRECATED: /submit/sub is deprecated now, so raise email to the admin (but allow submission to continue anyway)"""
args = wash_urlargd(form, {'password': (str, '')})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../sub/",
navmenuid='submit')
try:
raise DeprecationWarning, 'submit/sub handler has been used. Please use submit/direct. e.g. "submit/sub?RN=123@SBIFOO" -> "submit/direct?RN=123&sub=SBIFOO"'
except DeprecationWarning:
register_exception(req=req, alert_admin=True)
ln = args['ln']
_ = gettext_set_language(ln)
#DEMOBOO_RN=DEMO-BOOK-2008-001&ln=en&password=1223993532.26572%40APPDEMOBOO
params = dict(form)
password = args['password']
if password:
del params['password']
if "@" in password:
params['access'], params['sub'] = password.split('@', 1)
else:
params['sub'] = password
else:
args = str(req.args).split('@')
if len(args) > 1:
params = {'sub' : args[-1]}
args = '@'.join(args[:-1])
params.update(cgi.parse_qs(args))
else:
return warningMsg(_("Sorry, invalid URL..."), req, ln=ln)
url = "%s/submit/direct?%s" % (CFG_SITE_URL, urlencode(params, doseq=True))
redirect_to_url(req, url)
def summary(self, req, form):
args = wash_urlargd(form, {
'doctype': (str, ''),
'act': (str, ''),
'access': (str, ''),
'indir': (str, '')})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../summary",
navmenuid='submit')
t=""
curdir = os.path.join(CFG_WEBSUBMIT_STORAGEDIR, args['indir'], args['doctype'], args['access'])
try:
assert(curdir == os.path.abspath(curdir))
except AssertionError:
register_exception(req=req, alert_admin=True, prefix='Possible cracking tentative: indir="%s", doctype="%s", access="%s"' % (args['indir'], args['doctype'], args['access']))
return warningMsg("Invalid parameters", req)
subname = "%s%s" % (args['act'], args['doctype'])
res = run_sql("select sdesc,fidesc,pagenb,level from sbmFIELD where subname=%s "
"order by pagenb,fieldnb", (subname,))
nbFields = 0
values = []
for arr in res:
if arr[0] != "":
val = {
'mandatory' : (arr[3] == 'M'),
'value' : '',
'page' : arr[2],
'name' : arr[0],
}
if os.path.exists(os.path.join(curdir, curdir,arr[1])):
fd = open(os.path.join(curdir, arr[1]),"r")
value = fd.read()
fd.close()
value = value.replace("\n"," ")
value = value.replace("Select:","")
else:
value = ""
val['value'] = value
values.append(val)
return websubmit_templates.tmpl_submit_summary(
ln = args['ln'],
values = values,
)
def index(self, req, form):
args = wash_urlargd(form, {
'c': (str, CFG_SITE_NAME),
'doctype': (str, ''),
'act': (str, ''),
'startPg': (str, "1"),
'access': (str, ''),
'mainmenu': (str, ''),
'fromdir': (str, ''),
'nextPg': (str, ''),
'nbPg': (str, ''),
'curpage': (str, '1'),
'step': (str, '0'),
'mode': (str, 'U'),
})
## Strip whitespace from beginning and end of doctype and action:
args["doctype"] = args["doctype"].strip()
args["act"] = args["act"].strip()
def _index(req, c, ln, doctype, act, startPg, access,
mainmenu, fromdir, nextPg, nbPg, curpage, step,
mode):
uid = getUid(req)
if isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : CFG_SITE_URL + req.unparsed_uri, 'ln' : args['ln']}, {})), norobot=True)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../submit",
navmenuid='submit')
if doctype=="":
return home(req,c,ln)
elif act=="":
return action(req,c,ln,doctype)
elif int(step)==0:
return interface(req, c, ln, doctype, act, startPg, access, mainmenu, fromdir, nextPg, nbPg, curpage)
else:
return endaction(req, c, ln, doctype, act, startPg, access,mainmenu, fromdir, nextPg, nbPg, curpage, step, mode)
return _index(req, **args)
# Answer to both /submit/ and /submit
__call__ = index
def errorMsg(title, req, c=None, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
if c is None:
c = CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)
return page(title = _("Error"),
body = create_error_box(req, title=str(title), verbose=0, ln=ln),
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
def warningMsg(title, req, c=None, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
if c is None:
c = CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)
return page(title = _("Warning"),
body = title,
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
def print_warning(msg, type='', prologue=' ', epilogue=' '):
"""Prints warning message and flushes output."""
if msg:
return websubmit_templates.tmpl_print_warning(
msg = msg,
type = type,
prologue = prologue,
epilogue = epilogue,
)
else:
return ''
def retrieve_most_recent_attached_file(file_path):
"""
Retrieve the latest file that has been uploaded with the
FCKeditor. This is the only way to retrieve files that the
FCKeditor has renamed after the upload.
Eg: 'prefix/image.jpg' was uploaded but did already
exist. FCKeditor silently renamed it to 'prefix/image(1).jpg':
>>> retrieve_most_recent_attached_file('prefix/image.jpg')
'prefix/image(1).jpg'
"""
(base_path, filename) = os.path.split(file_path)
base_name = os.path.splitext(filename)[0]
file_ext = os.path.splitext(filename)[1][1:]
most_recent_filename = filename
i = 0
while True:
i += 1
possible_filename = "%s(%d).%s" % \
(base_name, i, file_ext)
if os.path.exists(base_path + os.sep + possible_filename):
most_recent_filename = possible_filename
else:
break
return os.path.join(base_path, most_recent_filename)