diff --git a/config/invenio-autotools.conf.in b/config/invenio-autotools.conf.in index 37897bd71..fb1a46ab0 100644 --- a/config/invenio-autotools.conf.in +++ b/config/invenio-autotools.conf.in @@ -1,81 +1,82 @@ ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ## DO NOT EDIT THIS FILE. ## YOU SHOULD NOT EDIT THESE VALUES. THEY WERE AUTOMATICALLY ## CALCULATED BY AUTOTOOLS DURING THE "CONFIGURE" STAGE. [Invenio] ## Invenio version: CFG_VERSION = @VERSION@ ## directories detected from 'configure --prefix ...' parameters: CFG_PREFIX = @prefix@ CFG_BINDIR = @prefix@/bin CFG_PYLIBDIR = @prefix@/lib/python CFG_LOGDIR = @localstatedir@/log CFG_ETCDIR = @prefix@/etc CFG_LOCALEDIR = @prefix@/share/locale CFG_TMPDIR = @localstatedir@/tmp CFG_CACHEDIR = @localstatedir@/cache CFG_WEBDIR = @localstatedir@/www ## path to interesting programs: CFG_PATH_MYSQL = @MYSQL@ CFG_PATH_PHP = @PHP@ CFG_PATH_GZIP = @GZIP@ CFG_PATH_GUNZIP = @GUNZIP@ CFG_PATH_TAR = @TAR@ CFG_PATH_GFILE = @FILE@ CFG_PATH_CONVERT = @CONVERT@ CFG_PATH_PDFTOTEXT = @PDFTOTEXT@ CFG_PATH_PDFTK = @PDFTK@ CFG_PATH_PDFTOPS = @PDFTOPS@ CFG_PATH_PDF2PS = @PDF2PS@ CFG_PATH_PDFINFO = @PDFINFO@ CFG_PATH_PDFTOPPM = @PDFTOPPM@ CFG_PATH_PAMFILE = @PAMFILE@ CFG_PATH_GS = @GS@ CFG_PATH_PS2PDF = @PS2PDF@ +CFG_PATH_PDFLATEX = @PDFLATEX@ CFG_PATH_PDFOPT = @PDFOPT@ CFG_PATH_PSTOTEXT = @PSTOTEXT@ CFG_PATH_PSTOASCII = @PSTOASCII@ CFG_PATH_ANY2DJVU = @ANY2DJVU@ CFG_PATH_DJVUPS = @DJVUPS@ CFG_PATH_DJVUTXT = @DJVUTXT@ CFG_PATH_TIFF2PDF = @TIFF2PDF@ CFG_PATH_OCROSCRIPT = @OCROSCRIPT@ CFG_PATH_OPENOFFICE_PYTHON = @OPENOFFICE_PYTHON@ CFG_PATH_WGET = @WGET@ CFG_PATH_MD5SUM = @MD5SUM@ ## CFG_BIBINDEX_PATH_TO_STOPWORDS_FILE -- path to the stopwords file. You ## probably don't want to change this path, although you may want to ## change the content of that file. Note that the file is used by the ## rank engine internally, so it should be given even if stopword ## removal in the indexes is not used. CFG_BIBINDEX_PATH_TO_STOPWORDS_FILE = @prefix@/etc/bibrank/stopwords.kb ## helper style of variables for WebSubmit: CFG_WEBSUBMIT_COUNTERSDIR = @localstatedir@/data/submit/counters CFG_WEBSUBMIT_STORAGEDIR = @localstatedir@/data/submit/storage CFG_WEBSUBMIT_FILEDIR = @localstatedir@/data/files CFG_WEBSUBMIT_BIBCONVERTCONFIGDIR = @prefix@/etc/bibconvert/config ## - end of file - diff --git a/configure.ac b/configure.ac index 75519f326..f0c474d36 100644 --- a/configure.ac +++ b/configure.ac @@ -1,806 +1,818 @@ ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ## This is CDS Invenio main configure.ac file. If you change this ## file, then please run "autoreconf" to regenerate the "configure" ## script. ## Initialize autoconf and automake: AC_INIT([cds-invenio], m4_esyscmd([./git-version-gen .tarball-version]), [cds.support@cern.ch]) AM_INIT_AUTOMAKE([tar-ustar]) ## By default we shall install into /opt/cds-invenio. (Do not use ## AC_PREFIX_DEFAULT for this, because it would not work well with ## the localstatedir hack below.) test "${prefix}" = NONE && prefix=/opt/cds-invenio ## Remove eventual trailing slashes from the prefix value: test "${prefix%/}" != "" && prefix=${prefix%/} ## Check for install: AC_PROG_INSTALL ## Check for gettext support: AM_GNU_GETTEXT(external) AM_GNU_GETTEXT_VERSION(0.14.4) ## Check for MySQL client: AC_MSG_CHECKING(for mysql) AC_ARG_WITH(mysql, AC_HELP_STRING([--with-mysql], [path to a specific MySQL binary (optional)]), MYSQL=${withval}) if test -n "$MYSQL"; then AC_MSG_RESULT($MYSQL) else AC_PATH_PROG(MYSQL, mysql) if test -z "$MYSQL"; then AC_MSG_ERROR([ MySQL command-line client was not found in your PATH. Please install it first. Available from .]) fi fi ## Check for Python: AC_MSG_CHECKING(for python) AC_ARG_WITH(python, AC_HELP_STRING([--with-python], [path to a specific Python binary (optional)]), PYTHON=${withval}) if test -n "$PYTHON"; then AC_MSG_RESULT($PYTHON) else AC_PATH_PROG(PYTHON, python) if test -z "$PYTHON"; then AC_MSG_ERROR([ Python was not found in your PATH. Please either install it in your PATH or specify --with-python configure option. Python is available from .]) fi fi ## Check for OpenOffice.org Python binary: AC_MSG_CHECKING(for OpenOffice.org Python binary) AC_ARG_WITH(openoffice-python, AC_HELP_STRING([--with-openoffice-python], [path to a specific OpenOffice.org Python binary (optional)]), OPENOFFICE_PYTHON=`which ${withval}`) if test -z "$OPENOFFICE_PYTHON"; then OPENOFFICE_PYTHON=`locate -n 1 -r "o.*office/program/python$"` OPENOFFICE_PYTHON="$PYTHON $OPENOFFICE_PYTHON" if test -n "$OPENOFFICE_PYTHON" && ($OPENOFFICE_PYTHON -c "import uno" 2> /dev/null); then AC_MSG_RESULT($OPENOFFICE_PYTHON) else AC_MSG_WARN([ You have not specified the path ot the OpenOffice.org Python binary. OpenOffice.org and Microsoft Office document conversion and fulltext indexing will not be available. We recommend you to install OpenOffice.org first and to rerun the configure script. OpenOffice.org is available from .]) fi elif ($OPENOFFICE_PYTHON -c "import uno" 2> /dev/null); then AC_MSG_RESULT($OPENOFFICE_PYTHON) else AC_MSG_ERROR([ The specified OpenOffice.org Python binary is not correctly configured. Please specify the correct path to the specific OpenOffice Python binary (OpenOffice.org is available from ).]) fi ## Check for Python version and modules: AC_MSG_CHECKING(for required Python modules) $PYTHON ${srcdir}/configure-tests.py if test $? -ne 0; then AC_MSG_ERROR([Please fix the above Python problem before continuing.]) fi AC_MSG_RESULT(found) ## Check for PHP: AC_PATH_PROG(PHP, php) ## Check for gzip: AC_PATH_PROG(GZIP, gzip) if test -z "$GZIP"; then AC_MSG_WARN([ Gzip was not found in your PATH. It is used in the WebSubmit module to compress the data submitted in an archive. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. Gzip is available from .]) fi ## Check for gunzip: AC_PATH_PROG(GUNZIP, gunzip) if test -z "$GUNZIP"; then AC_MSG_WARN([ Gunzip was not found in your PATH. It is used in the WebSubmit module to correctly deal with submitted compressed files. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. Gunzip is available from .]) fi ## Check for tar: AC_PATH_PROG(TAR, tar) if test -z "$TAR"; then AC_MSG_WARN([ Tar was not found in your PATH. It is used in the WebSubmit module to pack the submitted data into an archive. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. Tar is available from .]) fi ## Check for wget: AC_PATH_PROG(WGET, wget) if test -z "$WGET"; then AC_MSG_WARN([ wget was not found in your PATH. It is used for the fulltext file retrieval. You can continue without it but we recomend you to install it first and to rerun the configure script. wget is available from .]) fi ## Check for md5sum: AC_PATH_PROG(MD5SUM, md5sum) if test -z "$MD5SUM"; then AC_MSG_WARN([ md5sum was not found in your PATH. It is used for the fulltext file checksum verification. You can continue without it but we recomend you to install it first and to rerun the configure script. md5sum is available from .]) fi ## Check for ps2pdf: AC_PATH_PROG(PS2PDF, ps2pdf) if test -z "$PS2PDF"; then AC_MSG_WARN([ ps2pdf was not found in your PATH. It is used in the WebSubmit module to convert submitted PostScripts into PDF. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. ps2pdf is available from .]) fi +## Check for pdflatex: +AC_PATH_PROG(PDFLATEX, pdflatex) +if test -z "$PDFLATEX"; then + AC_MSG_WARN([ + pdflatex was not found in your PATH. It is used in + the WebSubmit module to stamp PDF files. + You can continue without it but you will miss some CDS Invenio + functionality. We recommend you to install it first and to rerun + the configure script.]) +fi + + ## Check for tiff2pdf: AC_PATH_PROG(TIFF2PDF, tiff2pdf) if test -z "$TIFF2PDF"; then AC_MSG_WARN([ tiff2pdf was not found in your PATH. It is used in the WebSubmit module to convert submitted TIFF file into PDF. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. tiff2pdf is available from .]) fi ## Check for gs: AC_PATH_PROG(GS, gs) if test -z "$GS"; then AC_MSG_WARN([ gs was not found in your PATH. It is used in the WebSubmit module to convert submitted PostScripts into PDF. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. gs is available from .]) fi ## Check for pdftotext: AC_PATH_PROG(PDFTOTEXT, pdftotext) if test -z "$PDFTOTEXT"; then AC_MSG_WARN([ pdftotext was not found in your PATH. It is used for the fulltext indexation of PDF files. You can continue without it but you may miss fulltext searching capability of CDS Invenio. We recomend you to install it first and to rerun the configure script. pdftotext is available from . ]) fi ## Check for pdftotext: AC_PATH_PROG(PDFINFO, pdfinfo) if test -z "$PDFINFO"; then AC_MSG_WARN([ pdfinfo was not found in your PATH. It is used for gathering information on PDF files. You can continue without it but you may miss this feature of CDS Invenio. We recomend you to install it first and to rerun the configure script. pdftotext is available from . ]) fi ## Check for pdftk: AC_PATH_PROG(PDFTK, pdftk) if test -z "$PDFTK"; then AC_MSG_WARN([ pdftk was not found in your PATH. It is used for the fulltext file stamping. You can continue without it but you may miss this feature of CDS Invenio. We recomend you to install it first and to rerun the configure script. pdftk is available from . ]) fi ## Check for pdf2ps: AC_PATH_PROG(PDF2PS, pdf2ps) if test -z "$PDF2PS"; then AC_MSG_WARN([ pdf2ps was not found in your PATH. It is used in the WebSubmit module to convert submitted PDFs into PostScript. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. pdf2ps is available from .]) fi ## Check for pdftops: AC_PATH_PROG(PDFTOPS, pdftops) if test -z "$PDFTOPS"; then AC_MSG_WARN([ pdftops was not found in your PATH. It is used in the WebSubmit module to convert submitted PDFs into PostScript. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. pdftops is available from .]) fi ## Check for pdfopt: AC_PATH_PROG(PDFOPT, pdfopt) if test -z "$PDFOPT"; then AC_MSG_WARN([ pdfopt was not found in your PATH. It is used in the WebSubmit module to linearized submitted PDFs. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. pdfopt is available from .]) fi ## Check for pdfimages: AC_PATH_PROG(PDFTOPPM, pdftoppm) if test -z "$PDFTOPPM"; then AC_MSG_WARN([ pdftoppm was not found in your PATH. It is used in the WebSubmit module to extract images from PDFs for OCR. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. pdftoppm is available from .]) fi ## Check for pdfimages: AC_PATH_PROG(PAMFILE, pdftoppm) if test -z "$PAMFILE"; then AC_MSG_WARN([ pamfile was not found in your PATH. It is used in the WebSubmit module to retrieve the size of images extracted from PDFs for OCR. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. pamfile is available as part of the netpbm utilities from: .]) fi ## Check for ocroscript: AC_PATH_PROG(OCROSCRIPT, ocroscript) if test -z "$OCROSCRIPT"; then AC_MSG_WARN([ If you plan to run OCR on your PDFs, then please install ocroscript now. Otherwise you can safely continue. You have also an option to install ocroscript later and edit invenio-local.conf to let CDS Invenio know the path to ocroscript. ocroscript is available as part of OCROpus from . NOTE: Since OCROpus is being actively developed and its api is continuosly changing, please install relase 0.3.1]) fi ## Check for pstotext: AC_PATH_PROG(PSTOTEXT, pstotext) if test -z "$PSTOTEXT"; then AC_MSG_WARN([ pstotext was not found in your PATH. It is used for the fulltext indexation of PDF and PostScript files. Please install pstotext. Otherwise you can safely continue. You have also an option to install pstotext later and edit invenio-local.conf to let CDS Invenio know the path to pstotext. pstotext is available from . ]) fi ## Check for ps2ascii: AC_PATH_PROG(PSTOASCII, ps2ascii) if test -z "$PSTOASCII"; then AC_MSG_WARN([ ps2ascii was not found in your PATH. It is used for the fulltext indexation of PostScript files. Please install ps2ascii. Otherwise you can safely continue. You have also an option to install ps2ascii later and edit invenio-local.conf to let CDS Invenio know the path to ps2ascii. ps2ascii is available from . ]) fi ## Check for any2djvu: AC_PATH_PROG(ANY2DJVU, any2djvu) if test -z "$ANY2DJVU"; then AC_MSG_WARN([ any2djvu was not found in your PATH. It is used in the WebSubmit module to convert documents to DJVU. Please install any2djvu. Otherwise you can safely continue. You have also an option to install any2djvu later and edit invenio-local.conf to let CDS Invenio know the path to any2djvu. any2djvu is available from .]) fi ## Check for DJVUPS: AC_PATH_PROG(DJVUPS, djvups) if test -z "$DJVUPS"; then AC_MSG_WARN([ djvups was not found in your PATH. It is used in the WebSubmit module to convert documents from DJVU. Please install djvups. Otherwise you can safely continue. You have also an option to install djvups later and edit invenio-local.conf to let CDS Invenio know the path to djvups. djvups is available from .]) fi ## Check for DJVUTXT: AC_PATH_PROG(DJVUTXT, djvutxt) if test -z "$DJVUTXT"; then AC_MSG_WARN([ djvutxt was not found in your PATH. It is used in the WebSubmit module to extract text from DJVU documents. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. djvutxt is available from .]) fi ## Check for file: AC_PATH_PROG(FILE, file) if test -z "$FILE"; then AC_MSG_WARN([ File was not found in your PATH. It is used in the WebSubmit module to check the validity of the submitted files. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. File is available from .]) fi ## Check for convert: AC_PATH_PROG(CONVERT, convert) if test -z "$CONVERT"; then AC_MSG_WARN([ Convert was not found in your PATH. It is used in the WebSubmit module to create an icon from a submitted picture. You can continue without it but you will miss some CDS Invenio functionality. We recommend you to install it first and to rerun the configure script. Convert is available from .]) fi ## Check for CLISP: AC_MSG_CHECKING(for clisp) AC_ARG_WITH(clisp, AC_HELP_STRING([--with-clisp], [path to a specific CLISP binary (optional)]), CLISP=${withval}) if test -n "$CLISP"; then AC_MSG_RESULT($CLISP) else AC_PATH_PROG(CLISP, clisp) if test -z "$CLISP"; then AC_MSG_WARN([ GNU CLISP was not found in your PATH. It is used by the WebStat module to produce statistics about CDS Invenio usage. (Alternatively, SBCL or CMUCL can be used instead of CLISP.) You can continue without it but you will miss this feature. We recommend you to install it first (if you don't have neither CMUCL nor SBCL) and to rerun the configure script. GNU CLISP is available from .]) fi fi ## Check for CMUCL: AC_MSG_CHECKING(for cmucl) AC_ARG_WITH(cmucl, AC_HELP_STRING([--with-cmucl], [path to a specific CMUCL binary (optional)]), CMUCL=${withval}) if test -n "$CMUCL"; then AC_MSG_RESULT($CMUCL) else AC_PATH_PROG(CMUCL, cmucl) if test -z "$CMUCL"; then AC_MSG_CHECKING(for lisp) # CMUCL can also be installed under `lisp' exec name AC_PATH_PROG(CMUCL, lisp) fi if test -z "$CMUCL"; then AC_MSG_WARN([ CMUCL was not found in your PATH. It is used by the WebStat module to produce statistics about CDS Invenio usage. (Alternatively, CLISP or SBCL can be used instead of CMUCL.) You can continue without it but you will miss this feature. We recommend you to install it first (if you don't have neither CLISP nor SBCL) and to rerun the configure script. CMUCL is available from .]) fi fi ## Check for SBCL: AC_MSG_CHECKING(for sbcl) AC_ARG_WITH(sbcl, AC_HELP_STRING([--with-sbcl], [path to a specific SBCL binary (optional)]), SBCL=${withval}) if test -n "$SBCL"; then AC_MSG_RESULT($SBCL) else AC_PATH_PROG(SBCL, sbcl) if test -z "$SBCL"; then AC_MSG_WARN([ SBCL was not found in your PATH. It is used by the WebStat module to produce statistics about CDS Invenio usage. (Alternatively, CLISP or CMUCL can be used instead of SBCL.) You can continue without it but you will miss this feature. We recommend you to install it first (if you don't have neither CLISP nor CMUCL) and to rerun the configure script. SBCL is available from .]) fi fi ## Check for gnuplot: AC_PATH_PROG(GNUPLOT, gnuplot) if test -z "$GNUPLOT"; then AC_MSG_WARN([ Gnuplot was not found in your PATH. It is used by the BibRank module to produce graphs about download and citation history. You can continue without it but you will miss these graphs. We recommend you to install it first and to rerun the configure script. Gnuplot is available from .]) fi ## Substitute variables: AC_SUBST(VERSION) AC_SUBST(OPENOFFICE_PYTHON) AC_SUBST(MYSQL) AC_SUBST(PYTHON) AC_SUBST(GZIP) AC_SUBST(GUNZIP) AC_SUBST(TAR) AC_SUBST(WGET) AC_SUBST(MD5SUM) AC_SUBST(PS2PDF) AC_SUBST(GS) AC_SUBST(PDFTOTEXT) AC_SUBST(PDFTK) AC_SUBST(PDF2PS) AC_SUBST(PDFTOPS) AC_SUBST(PDFOPT) AC_SUBST(PDFTOPPM) AC_SUBST(OCROSCRIPT) AC_SUBST(PSTOTEXT) AC_SUBST(PSTOASCII) AC_SUBST(ANY2DJVU) AC_SUBST(DJVUPS) AC_SUBST(DJVUTXT) AC_SUBST(FILE) AC_SUBST(CONVERT) AC_SUBST(GNUPLOT) AC_SUBST(CLISP) AC_SUBST(CMUCL) AC_SUBST(SBCL) AC_SUBST(CACHEDIR) AC_SUBST(localstatedir, `eval echo "${localstatedir}"`) ## Define output files: AC_CONFIG_FILES([config.nice \ Makefile \ po/Makefile.in \ config/Makefile \ config/invenio-autotools.conf \ modules/Makefile \ modules/bibcatalog/Makefile \ modules/bibcatalog/doc/Makefile \ modules/bibcatalog/doc/admin/Makefile \ modules/bibcatalog/doc/hacking/Makefile modules/bibcatalog/lib/Makefile \ modules/bibcheck/Makefile \ modules/bibcheck/doc/Makefile \ modules/bibcheck/doc/admin/Makefile \ modules/bibcheck/doc/hacking/Makefile \ modules/bibcheck/etc/Makefile \ modules/bibcheck/web/Makefile \ modules/bibcheck/web/admin/Makefile \ modules/bibcirculation/Makefile \ modules/bibcirculation/bin/Makefile \ modules/bibcirculation/doc/Makefile \ modules/bibcirculation/doc/admin/Makefile \ modules/bibcirculation/doc/hacking/Makefile modules/bibcirculation/lib/Makefile \ modules/bibcirculation/web/Makefile \ modules/bibcirculation/web/admin/Makefile \ modules/bibclassify/Makefile \ modules/bibclassify/bin/Makefile \ modules/bibclassify/bin/bibclassify \ modules/bibclassify/doc/Makefile \ modules/bibclassify/doc/admin/Makefile \ modules/bibclassify/doc/hacking/Makefile \ modules/bibclassify/lib/Makefile \ modules/bibconvert/Makefile \ modules/bibconvert/bin/Makefile \ modules/bibconvert/bin/bibconvert \ modules/bibconvert/doc/Makefile \ modules/bibconvert/doc/admin/Makefile \ modules/bibconvert/doc/hacking/Makefile \ modules/bibconvert/etc/Makefile \ modules/bibconvert/lib/Makefile \ modules/bibedit/Makefile \ modules/bibedit/bin/Makefile \ modules/bibedit/bin/bibedit \ modules/bibedit/bin/refextract \ modules/bibedit/bin/xmlmarc2textmarc \ modules/bibedit/bin/textmarc2xmlmarc \ modules/bibedit/bin/xmlmarclint \ modules/bibedit/doc/Makefile \ modules/bibedit/doc/admin/Makefile \ modules/bibedit/doc/hacking/Makefile \ modules/bibedit/etc/Makefile \ modules/bibedit/lib/Makefile \ modules/bibedit/web/Makefile \ modules/bibexport/Makefile \ modules/bibexport/bin/Makefile \ modules/bibexport/bin/bibexport \ modules/bibexport/doc/Makefile \ modules/bibexport/doc/admin/Makefile \ modules/bibexport/doc/hacking/Makefile modules/bibexport/etc/Makefile \ modules/bibexport/lib/Makefile \ modules/bibexport/web/Makefile \ modules/bibexport/web/admin/Makefile \ modules/bibformat/Makefile \ modules/bibformat/bin/Makefile \ modules/bibformat/bin/bibreformat \ modules/bibformat/doc/Makefile \ modules/bibformat/doc/admin/Makefile \ modules/bibformat/doc/hacking/Makefile \ modules/bibformat/etc/Makefile \ modules/bibformat/etc/format_templates/Makefile \ modules/bibformat/etc/output_formats/Makefile \ modules/bibformat/lib/Makefile \ modules/bibformat/lib/elements/Makefile \ modules/bibformat/web/Makefile \ modules/bibformat/web/admin/Makefile \ modules/bibharvest/Makefile \ modules/bibharvest/bin/Makefile \ modules/bibharvest/bin/oairepositoryupdater \ modules/bibharvest/bin/oaiharvest \ modules/bibharvest/doc/Makefile \ modules/bibharvest/doc/admin/Makefile \ modules/bibharvest/doc/hacking/Makefile \ modules/bibharvest/lib/Makefile \ modules/bibharvest/web/Makefile \ modules/bibharvest/web/admin/Makefile \ modules/bibindex/Makefile \ modules/bibindex/bin/Makefile \ modules/bibindex/bin/bibindex \ modules/bibindex/bin/bibstat \ modules/bibindex/doc/Makefile \ modules/bibindex/doc/admin/Makefile \ modules/bibindex/doc/hacking/Makefile \ modules/bibindex/lib/Makefile \ modules/bibindex/web/Makefile \ modules/bibindex/web/admin/Makefile \ modules/bibknowledge/Makefile \ modules/bibknowledge/lib/Makefile \ modules/bibknowledge/doc/Makefile \ modules/bibknowledge/doc/admin/Makefile \ modules/bibknowledge/doc/hacking/Makefile \ modules/bibmatch/Makefile \ modules/bibmatch/bin/Makefile \ modules/bibmatch/bin/bibmatch \ modules/bibmatch/doc/Makefile \ modules/bibmatch/doc/admin/Makefile \ modules/bibmatch/etc/Makefile \ modules/bibmatch/lib/Makefile \ modules/bibmerge/Makefile \ modules/bibmerge/bin/Makefile \ modules/bibmerge/doc/Makefile \ modules/bibmerge/doc/admin/Makefile \ modules/bibmerge/doc/hacking/Makefile \ modules/bibmerge/lib/Makefile \ modules/bibmerge/web/Makefile \ modules/bibmerge/web/admin/Makefile \ modules/bibrank/Makefile \ modules/bibrank/bin/Makefile \ modules/bibrank/bin/bibrank \ modules/bibrank/bin/bibrankgkb \ modules/bibrank/doc/Makefile \ modules/bibrank/doc/admin/Makefile \ modules/bibrank/doc/hacking/Makefile \ modules/bibrank/etc/Makefile \ modules/bibrank/etc/bibrankgkb.cfg \ modules/bibrank/etc/demo_jif.cfg \ modules/bibrank/etc/template_single_tag_rank_method.cfg \ modules/bibrank/lib/Makefile \ modules/bibrank/web/Makefile \ modules/bibrank/web/admin/Makefile \ modules/bibsched/Makefile \ modules/bibsched/bin/Makefile \ modules/bibsched/bin/bibsched \ modules/bibsched/bin/bibtaskex \ modules/bibsched/doc/Makefile \ modules/bibsched/doc/admin/Makefile \ modules/bibsched/doc/hacking/Makefile \ modules/bibsched/lib/Makefile \ modules/bibupload/Makefile \ modules/bibupload/bin/Makefile \ modules/bibupload/bin/bibupload \ modules/bibupload/bin/batchuploader \ modules/bibupload/doc/Makefile \ modules/bibupload/doc/admin/Makefile \ modules/bibupload/doc/hacking/Makefile \ modules/bibupload/lib/Makefile \ modules/elmsubmit/Makefile \ modules/elmsubmit/bin/Makefile \ modules/elmsubmit/bin/elmsubmit \ modules/elmsubmit/doc/Makefile \ modules/elmsubmit/doc/admin/Makefile \ modules/elmsubmit/doc/hacking/Makefile \ modules/elmsubmit/etc/Makefile \ modules/elmsubmit/etc/elmsubmit.cfg \ modules/elmsubmit/lib/Makefile \ modules/miscutil/Makefile \ modules/miscutil/bin/Makefile \ modules/miscutil/bin/dbdump \ modules/miscutil/bin/dbexec \ modules/miscutil/bin/inveniocfg \ modules/miscutil/bin/plotextractor \ modules/miscutil/demo/Makefile \ modules/miscutil/doc/Makefile \ modules/miscutil/doc/hacking/Makefile \ modules/miscutil/etc/Makefile \ modules/miscutil/etc/bash_completion.d/Makefile \ modules/miscutil/etc/bash_completion.d/inveniocfg \ modules/miscutil/lib/Makefile \ modules/miscutil/sql/Makefile \ modules/miscutil/web/Makefile \ modules/webaccess/Makefile \ modules/webaccess/bin/Makefile \ modules/webaccess/bin/authaction \ modules/webaccess/bin/webaccessadmin \ modules/webaccess/doc/Makefile \ modules/webaccess/doc/admin/Makefile \ modules/webaccess/doc/hacking/Makefile \ modules/webaccess/lib/Makefile \ modules/webaccess/web/Makefile \ modules/webaccess/web/admin/Makefile \ modules/webalert/Makefile \ modules/webalert/bin/Makefile \ modules/webalert/bin/alertengine \ modules/webalert/doc/Makefile \ modules/webalert/doc/admin/Makefile \ modules/webalert/doc/hacking/Makefile \ modules/webalert/lib/Makefile \ modules/webalert/web/Makefile \ modules/webbasket/Makefile \ modules/webbasket/doc/Makefile \ modules/webbasket/doc/admin/Makefile \ modules/webbasket/doc/hacking/Makefile \ modules/webbasket/lib/Makefile \ modules/webbasket/web/Makefile \ modules/webcomment/Makefile \ modules/webcomment/doc/Makefile \ modules/webcomment/doc/admin/Makefile \ modules/webcomment/doc/hacking/Makefile \ modules/webcomment/lib/Makefile \ modules/webcomment/web/Makefile \ modules/webcomment/web/admin/Makefile \ modules/webhelp/Makefile \ modules/webhelp/web/Makefile \ modules/webhelp/web/admin/Makefile \ modules/webhelp/web/admin/howto/Makefile \ modules/webhelp/web/hacking/Makefile \ modules/webjournal/Makefile \ modules/webjournal/etc/Makefile \ modules/webjournal/doc/Makefile \ modules/webjournal/doc/admin/Makefile \ modules/webjournal/doc/hacking/Makefile \ modules/webjournal/lib/Makefile \ modules/webjournal/lib/elements/Makefile \ modules/webjournal/lib/widgets/Makefile \ modules/webjournal/web/Makefile \ modules/webjournal/web/admin/Makefile \ modules/webmessage/Makefile \ modules/webmessage/bin/Makefile \ modules/webmessage/bin/webmessageadmin \ modules/webmessage/doc/Makefile \ modules/webmessage/doc/admin/Makefile \ modules/webmessage/doc/hacking/Makefile \ modules/webmessage/lib/Makefile \ modules/webmessage/web/Makefile \ modules/websearch/Makefile \ modules/websearch/bin/Makefile \ modules/websearch/bin/webcoll \ modules/websearch/doc/Makefile \ modules/websearch/doc/admin/Makefile \ modules/websearch/doc/hacking/Makefile \ modules/websearch/lib/Makefile \ modules/websearch/web/Makefile \ modules/websearch/web/admin/Makefile \ modules/websession/Makefile \ modules/websession/bin/Makefile \ modules/websession/bin/inveniogc \ modules/websession/doc/Makefile \ modules/websession/doc/admin/Makefile \ modules/websession/doc/hacking/Makefile \ modules/websession/lib/Makefile \ modules/websession/web/Makefile \ modules/webstat/Makefile \ modules/webstat/bin/Makefile \ modules/webstat/bin/webstat \ modules/webstat/bin/webstatadmin \ modules/webstat/doc/Makefile \ modules/webstat/doc/admin/Makefile \ modules/webstat/doc/hacking/Makefile \ modules/webstat/etc/Makefile \ modules/webstat/lib/Makefile \ modules/webstyle/Makefile \ modules/webstyle/bin/Makefile \ modules/webstyle/bin/webdoc \ modules/webstyle/css/Makefile \ modules/webstyle/doc/Makefile \ modules/webstyle/doc/admin/Makefile \ modules/webstyle/doc/hacking/Makefile \ modules/webstyle/etc/Makefile \ modules/webstyle/img/Makefile \ modules/webstyle/lib/Makefile \ modules/websubmit/Makefile \ modules/websubmit/bin/Makefile \ modules/websubmit/bin/bibdocfile \ modules/websubmit/doc/Makefile \ modules/websubmit/doc/admin/Makefile \ modules/websubmit/doc/hacking/Makefile \ modules/websubmit/etc/Makefile \ modules/websubmit/lib/Makefile \ modules/websubmit/lib/functions/Makefile \ modules/websubmit/web/Makefile \ modules/websubmit/web/admin/Makefile \ ]) ## Finally, write output files: AC_OUTPUT ## Write help: AC_MSG_RESULT([****************************************************************************]) AC_MSG_RESULT([** Your CDS Invenio installation is now ready for building. **]) AC_MSG_RESULT([** You have entered the following parameters: **]) AC_MSG_RESULT([** - CDS Invenio main install directory: ${prefix}]) AC_MSG_RESULT([** - Python executable: $PYTHON]) AC_MSG_RESULT([** - MySQL client executable: $MYSQL]) AC_MSG_RESULT([** - CLISP executable: $CLISP]) AC_MSG_RESULT([** - CMUCL executable: $CMUCL]) AC_MSG_RESULT([** - SBCL executable: $SBCL]) AC_MSG_RESULT([** Here are the steps to continue the building process: **]) AC_MSG_RESULT([** 1) Type 'make' to build your CDS Invenio system. **]) AC_MSG_RESULT([** 2) Type 'make install' to install your CDS Invenio system. **]) AC_MSG_RESULT([** After that you can start customizing your installation as documented **]) AC_MSG_RESULT([** in the INSTALL file (i.e. edit invenio.conf, run inveniocfg, etc). **]) AC_MSG_RESULT([** Good luck, and thanks for choosing CDS Invenio. **]) AC_MSG_RESULT([** -- CDS Development Group **]) AC_MSG_RESULT([****************************************************************************]) ## end of file diff --git a/modules/webbasket/lib/webbasket_webinterface.py b/modules/webbasket/lib/webbasket_webinterface.py index bc8cb114c..940522bf5 100644 --- a/modules/webbasket/lib/webbasket_webinterface.py +++ b/modules/webbasket/lib/webbasket_webinterface.py @@ -1,1573 +1,1573 @@ ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """WebBasket Web Interface.""" __revision__ = "$Id$" __lastupdated__ = """$Date$""" from 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']: # 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('/opt/cds-invenio/var/data/baskets/comments/' + \ + 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('/opt/cds-invenio/var/data/baskets/comments/' + \ + 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']: # 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 86f23c7f8..fbbac0791 100644 --- a/modules/webcomment/lib/webcomment_webinterface.py +++ b/modules/webcomment/lib/webcomment_webinterface.py @@ -1,817 +1,818 @@ # -*- coding: utf-8 -*- ## Comments and reviews for records. ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """ Comments and reviews for records: web interface """ __lastupdated__ = """$Date$""" __revision__ = """$Id$""" import 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']: 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']): 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']): 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']: 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': 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']: 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']: 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('/opt/cds-invenio/var/data/comments/' + \ + 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('/opt/cds-invenio/var/data/comments/' + \ + 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']: ## # 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/webjournal/lib/webjournal_regression_tests.py b/modules/webjournal/lib/webjournal_regression_tests.py index ba5498c39..b83ec17a4 100644 --- a/modules/webjournal/lib/webjournal_regression_tests.py +++ b/modules/webjournal/lib/webjournal_regression_tests.py @@ -1,339 +1,340 @@ # -*- coding: utf-8 -*- ## ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """WebJournal Regression Test Suite.""" __revision__ = "$Id$" import datetime import unittest import urllib from invenio import webjournal_utils as wju from invenio.config import CFG_SITE_URL, \ CFG_SITE_LANG, \ - CFG_SITE_SUPPORT_EMAIL + CFG_SITE_SUPPORT_EMAIL, \ + CFG_PREFIX from invenio.testutils import make_test_suite, run_test_suite class ArticlesRelated(unittest.TestCase): """Functions about articles""" def test_is_new_article(self): """webjournal - checks if an article is new or not """ article = wju.is_new_article('AtlantisTimes', '03/2009', 99) self.assertEqual(article, False) article = wju.is_new_article('AtlantisTimes', '03/2009', 103) self.assertEqual(article, True) class CategoriesRelated(unittest.TestCase): """Functions about journal categories""" def test_get_journal_categories(self): """webjournal - returns all categories for a given issue""" journal1 = wju.get_journal_categories('AtlantisTimes', '03/2009') self.assertEqual(journal1[0], 'News') self.assertEqual(journal1[1], 'Science') journal2 = wju.get_journal_categories('AtlantisTimes', ) self.assertEqual(journal2[0], 'News') self.assertEqual(journal2[1], 'Science') self.assertEqual(journal2[2], 'Arts') def test_get_category_query(self): """webjournal - returns the category definition """ self.assertEqual(wju.get_category_query('AtlantisTimes', 'News'), '980__a:ATLANTISTIMESNEWS or 980__a:ATLANTISTIMESNEWSDRAFT') self.assertEqual(wju.get_category_query('AtlantisTimes', 'Science'), '980__a:ATLANTISTIMESSCIENCE or 980__a:ATLANTISTIMESSCIENCEDRAFT') class JournalConfigVars(unittest.TestCase): """Functions to get journal variables """ def test_get_xml_from_config(self): """webjournal - returns values from the journal configuration file """ value = wju.get_xml_from_config(["submission/doctype"], 'AtlantisTimes') self.assertEqual(value.values()[0], ['DEMOJRN']) self.assertEqual(value.keys(), ['submission/doctype']) value = wju.get_xml_from_config(["submission/identifier_element"], 'AtlantisTimes') self.assertEqual(value.values()[0], ['DEMOJRN_RN']) self.assertEqual(value.keys(), ['submission/identifier_element']) def test_get_journal_issue_field(self): """webjournal - returns the MARC field """ value = wju.get_journal_issue_field('AtlantisTimes') self.assertEqual(value, '773__n') def test_get_journal_css_url(self): """webjournal - returns URL to this journal's CSS """ self.assertEqual(wju.get_journal_css_url('AtlantisTimes', type='screen'), CFG_SITE_URL + '/img/AtlantisTimes.css') def test_get_journal_submission_params(self): """webjournal - returns params for the submission of articles """ submissions = wju.get_journal_submission_params('AtlantisTimes') self.assertEqual(submissions[0], 'DEMOJRN') self.assertEqual(submissions[1], 'DEMOJRN_RN') self.assertEqual(submissions[2], '037__a') def test_get_journal_draft_keyword_to_remove(self): """webjournal - returns the keyword to removed in order to move the article from Draft to Ready """ self.assertEqual(wju.get_journal_draft_keyword_to_remove('AtlantisTimes'), 'DRAFT') def test_get_journal_alert_sender_email(self): """webjournal - returns the email address used to send of the alert email. """ self.assertEqual(wju.get_journal_alert_sender_email('AtlantisTimes'), CFG_SITE_SUPPORT_EMAIL) def test_get_journal_alert_recipient_email(self): """webjournal - returns the default email address of the recipients of the email""" self.assertEqual(wju.get_journal_alert_recipient_email('AtlantisTimes'),'recipients@atlantis.atl') def test_get_journal_template(self): """webjournal - returns the journal templates name for the given template type""" value = wju.get_journal_template('index', 'AtlantisTimes', ln=CFG_SITE_LANG) self.assertEqual(value, 'webjournal/AtlantisTimes_Index.bft') def test_get_journal_name_intl(self): """webjournal - returns the nice name of the journal """ name = wju.get_journal_name_intl('AtlantisTimes', ln=CFG_SITE_LANG) self.assertEqual(name, 'Atlantis Times') def test_get_journal_languages(self): """webjournal - returns the list of languages defined for this journal""" lang = wju.get_journal_languages('AtlantisTimes') self.assertEqual(lang[0], 'en') self.assertEqual(lang[1], 'fr') def test_get_journal_issue_grouping(self): """webjournal - returns the number of issue that are typically released at the same time""" issue = wju.get_journal_issue_grouping('AtlantisTimes') self.assertEqual(issue, 2) def test_get_journal_nb_issues_per_year(self): """webjournal - returns the default number of issues per year for this journal""" nb = wju.get_journal_nb_issues_per_year('AtlantisTimes') self.assertEqual(nb, 52) def test_get_journal_preferred_language(self): """webjournal - returns the most adequate language to display the journal, given a language """ value = wju.get_journal_preferred_language('AtlantisTimes', 'fr') self.assertEqual(value, 'fr') value = wju.get_journal_preferred_language('AtlantisTimes', 'it') self.assertEqual(value, 'en') value = wju.get_journal_preferred_language('AtlantisTimes', 'hello') self.assertEqual(value, 'en') def test_get_unreleased_issue_hiding_mode(self): """webjournal - returns how unreleased issue should be treated""" value = wju.get_unreleased_issue_hiding_mode('AtlantisTimes') self.assertEqual(value, 'all') def test_get_first_issue_from_config(self): """webjournal - returns the first issue as defined from config""" issue = wju.get_first_issue_from_config('AtlantisTimes') self.assertEqual(issue, '02/2009') class TimeIssueFunctions(unittest.TestCase): """Functions about time, using issues""" def test_get_current_issue(self): """webjournal - returns the current issue of a journal """ issue = wju.get_current_issue('en', 'AtlantisTimes') self.assertEqual(issue, '03/2009') def test_get_all_released_issues(self): """webjournal - returns the list of released issue""" issues = wju.get_all_released_issues('AtlantisTimes') self.assertEqual(issues[0], '03/2009') self.assertEqual(issues[1], '02/2009') def test_get_next_journal_issues(self): """webjournal - this function suggests the 'n' next issue numbers """ issues = wju.get_next_journal_issues('03/2009', 'AtlantisTimes', n=2) self.assertEqual(issues[0], '04/2009') self.assertEqual(issues[1], '05/2009') def test_get_grouped_issues(self): """webjournal - returns all the issues grouped with a given one""" issues = wju.get_grouped_issues('AtlantisTimes', '03/2009') self.assertEqual(issues[0], '02/2009') self.assertEqual(issues[1], '03/2009') def test_get_issue_number_display(self): """webjournal - returns the display string for a given issue number""" issue_nb = wju.get_issue_number_display('03/2009', 'AtlantisTimes', ln=CFG_SITE_LANG) self.assertEqual(issue_nb, '02-03/2009') def test_make_issue_number(self): """webjournal - creates a normalized issue number representation""" issue = wju.make_issue_number('AtlantisTimes', 03, 2009, for_url_p=False) self.assertEqual(issue, '03/2009') issue = wju.make_issue_number('AtlantisTimes', 06, 2009, for_url_p=False) self.assertEqual(issue, '06/2009') issue = wju.make_issue_number('AtlantisTimes', 03, 2008, for_url_p=False) self.assertEqual(issue, '03/2008') def test_get_release_datetime(self): """webjournal - gets the date at which an issue was released from the DB""" value = wju.get_release_datetime('03/2009', 'AtlantisTimes', ln=CFG_SITE_LANG) self.assertEqual(value, datetime.datetime(2009, 1, 16, 0, 0)) def test_get_announcement_datetime(self): """webjournal - get the date at which an issue was announced through the alert system""" value = wju.get_announcement_datetime('03/2009', 'AtlantisTimes', ln=CFG_SITE_LANG) self.assertEqual(value, None) def test_datetime_to_issue(self): """webjournal - returns the issue corresponding to the given datetime object""" date_value = datetime.datetime(2009, 7, 16, 13, 39, 46, 426373) value = wju.datetime_to_issue(date_value, 'AtlantisTimes') self.assertEqual(value, None) def test_issue_to_datetime(self): """webjournal - returns the *theoretical* date of release for given issue""" issue = wju.issue_to_datetime('03/2009', 'AtlantisTimes', granularity=None) self.assertEqual(issue, datetime.datetime(2009, 1, 19, 0, 0)) def test_get_number_of_articles_for_issue(self): """webjournal - returns a dictionary with all categories and number of articles in each category""" value = wju.get_number_of_articles_for_issue('03/2009', 'AtlantisTimes', ln=CFG_SITE_LANG) self.assertEqual(value.values()[0], 3) self.assertEqual(value.values()[1], 1) self.assertEqual(value.keys()[0], 'News') self.assertEqual(value.keys()[1], 'Science') class JournalRelated(unittest.TestCase): """Functions about journal""" def test_get_journal_info_path(self): """webjournal - returns the path to the info file of the given journal""" info = wju.get_journal_info_path('AtlantisTimes') - path = '/opt/cds-invenio/var/cache/webjournal/AtlantisTimes/info.dat' + path = CFG_PREFIX + '/var/cache/webjournal/AtlantisTimes/info.dat' self.assertEqual(info, path) def test_get_journal_article_cache_path(self): """webjournal - returns the path to cache file of the articles of a given issue""" info = wju.get_journal_article_cache_path('AtlantisTimes', '03/2009') - path = '/opt/cds-invenio/var/cache/webjournal/AtlantisTimes/03_2009_articles_cache.dat' + path = CFG_PREFIX + '/var/cache/webjournal/AtlantisTimes/03_2009_articles_cache.dat' self.assertEqual(info, path) def test_get_journal_id(self): """webjournal - get the id for this journal from the DB""" jrnid = wju.get_journal_id('AtlantisTimes', ln=CFG_SITE_LANG) self.assertEqual(jrnid, 1) def test_guess_journal_name(self): """webjournal - tries to take a guess what a user was looking for on the server if not providing a name for the journal""" name = wju.guess_journal_name('en', journal_name=None) self.assertEqual(name, 'AtlantisTimes' ) def test_get_journals_ids_and_names(self): """webjournal - returns the list of existing journals IDs and names""" ids_names = wju.get_journals_ids_and_names() self.assertEqual(ids_names[0].values(), [1, 'AtlantisTimes']) self.assertEqual(ids_names[0].keys(), ['journal_id', 'journal_name']) def test_parse_url_string(self): """webjournal - parses any url string given in webjournal""" d = wju.parse_url_string("/journal/AtlantisTimes/2009/03/News/?ln=en") self.assertEqual(d['category'], 'News') self.assertEqual(d['issue_year'], 2009) self.assertEqual(d['ln'], 'en') self.assertEqual(d['issue_number'], 3) self.assertEqual(d['journal_name'], 'AtlantisTimes') self.assertEqual(d['issue'], '03/2009') d = wju.parse_url_string("/journal/AtlantisTimes/2009/03/Science?ln=en") self.assertEqual(d['category'], 'Science') self.assertEqual(d['issue_year'], 2009) self.assertEqual(d['ln'], 'en') self.assertEqual(d['issue_number'], 3) self.assertEqual(d['journal_name'], 'AtlantisTimes') self.assertEqual(d['issue'], '03/2009') d = wju.parse_url_string("/journal/AtlantisTimes/2009/03/News/97?ln=en") self.assertEqual(d['category'], 'News') self.assertEqual(d['issue_year'], 2009) self.assertEqual(d['ln'], 'en') self.assertEqual(d['issue_number'], 3) self.assertEqual(d['recid'], 97) self.assertEqual(d['journal_name'], 'AtlantisTimes') self.assertEqual(d['issue'], '03/2009') try: wju.parse_url_string("/journal/fictivejournal/2009/03/News/97?ln=en") dont_find_journal = 'not' except: dont_find_journal = 'ok' self.assertEqual(dont_find_journal, 'ok') class HtmlCachingFunction(unittest.TestCase): """HTML caching functions""" def setUp(self): "Access some URL for cache to be generated" urllib.urlopen(CFG_SITE_URL + '/journal/AtlantisTimes/2009/03/News') urllib.urlopen(CFG_SITE_URL + '/journal/AtlantisTimes/2009/03/News/103') def test_get_index_page_from_cache(self): """webjournal - function to get an index page from the cache""" value = wju.get_index_page_from_cache('AtlantisTimes', 'News', '03/2009', 'en') assert("Atlantis (Timaeus)" in value) def test_get_article_page_from_cache(self): """webjournal - gets an article view of a journal from cache""" value = wju.get_article_page_from_cache('AtlantisTimes', 'News', 103, '03/2009', 'en') assert("April 14th, 1832.—Leaving Socêgo, we rode to another estate on the Rio Macâe" in value) def test_clear_cache_for_issue(self): """webjournal - clears the cache of a whole issue""" value = wju.clear_cache_for_issue('AtlantisTimes', '03/2009') self.assertEqual(value, True) TEST_SUITE = make_test_suite(ArticlesRelated, CategoriesRelated, JournalConfigVars, TimeIssueFunctions, JournalRelated, HtmlCachingFunction) if __name__ == "__main__": run_test_suite(TEST_SUITE, warn_user=True) diff --git a/modules/websubmit/lib/websubmit_file_stamper.py b/modules/websubmit/lib/websubmit_file_stamper.py index 6b66c3af1..9cb7b0ee6 100644 --- a/modules/websubmit/lib/websubmit_file_stamper.py +++ b/modules/websubmit/lib/websubmit_file_stamper.py @@ -1,1594 +1,1599 @@ # -*- coding: utf-8 -*- ## ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """This is websubmit_file_stamper.py This tool is used to create a stamped version of a PDF file. + Python API: Please see stamp_file(). + CLI API: $ python ~invenio/lib/python/invenio/websubmit_file_stamper.py \\ --latex-template=demo-stamp-left.tex \\ --latex-template-var='REPORTNUMBER=TEST-THESIS-2008-019' \\ --latex-template-var='DATE=27/02/2008' \\ --stamp='first' \\ --layer='background' \\ --output-file=testfile_stamped.pdf \\ testfile.pdf """ __revision__ = "$Id$" import getopt, sys, re, os, time, shutil, tempfile -from invenio.config import CFG_PATH_PS2PDF, CFG_PATH_GFILE +from invenio.config import \ + CFG_PATH_PS2PDF, \ + CFG_PATH_GFILE,\ + CFG_PATH_PDFLATEX from invenio.errorlib import register_exception from invenio.config import CFG_TMPDIR from invenio.config import CFG_ETCDIR CFG_WEBSUBMIT_FILE_STAMPER_TEMPLATES_DIR = \ "%s/websubmit/file_stamper_templates" % CFG_ETCDIR from invenio.config import CFG_PATH_PDFTK from invenio.config import CFG_PATH_PDF2PS from invenio.shellutils import escape_shell_arg from invenio.websubmit_config import InvenioWebSubmitFileStamperError ## ***** Functions related to the creation of the PDF Stamp file: ***** re_latex_includegraphics = re.compile('\\includegraphics\[.*?\]\{(?P.*?)\}') def copy_template_files_to_stampdir(path_workingdir, latex_template): """In order to stamp a PDF fulltext file, LaTeX is used to create a "stamp" page that is then merged with the fulltext PDF. The stamp page is created in a temporary stamp "working directory". This means that the LaTeX file and its image files must be copied locally into this working directory. This function handles copying them into the working directory. Note: Copying of the LaTeX template and its included image files is fairly naive and assumes that it is a very basic LaTeX file consisting of a main file and any included graphics. No other included file items will be copied. Also note that the order of searching for the LaTeX file and its associated graphics is as follows: + If the templatename provided has a path attached to it, look here first; + If there is no path, look in the current dir. + If there is no template in the current dir, look in ~invenio/etc/websubmit/latex + Images included within the LaTeX file are sought in the same way. Full path is used if provided; if not, current dir and failing that ~invenio/etc/websubmit/latex. @param path_workingdir: (string) - the working directory into which the latex templates should be copied. @param latex_template: (string) - the name of the LaTeX template to copy to the working dir. """ ## Get the "base name" of the latex template: (template_path, template_name) = os.path.split(latex_template) if template_path != "": ## A full path to the template was provided. We look for it there. ## Test to see whether the template is a real file and is readable: if os.access("%s/%s" % (template_path, template_name), os.R_OK): ## Template is readable. Copy it locally to the working directory: try: shutil.copyfile("%s/%s" % (template_path, template_name), \ "%s/%s" % (path_workingdir, template_name)) except IOError: ## Unable to copy the LaTeX template file to the ## working directory: msg = """Error: Unable to copy LaTeX file [%s/%s] to """ \ """working directory for stamping [%s].""" \ % (template_path, template_name, path_workingdir) raise InvenioWebSubmitFileStamperError(msg) else: ## Unable to read the template file: msg = """Error: Unable to copy LaTeX file [%s/%s] to """ \ """working directory for stamping [%s]. (File not """ \ """readable.)""" \ % (template_path, template_name, path_workingdir) raise InvenioWebSubmitFileStamperError(msg) else: ## There is no path to the template file. ## Look for it first in the current working directory, then in ## ~invenio/websubmit/latex; ## If not found in either, give up. if os.access("%s" % (template_name), os.F_OK): ## Template has been found in the current working directory. ## Copy it locally to the stamping working directory: try: shutil.copyfile("%s" % (template_name), \ "%s/%s" % (path_workingdir, template_name)) except IOError: ## Unable to copy the LaTeX template file to the ## working stamping directory: msg = """Error: Unable to copy LaTeX file [%s] to """ \ """working directory for stamping [%s].""" \ % (template_name, path_workingdir) raise InvenioWebSubmitFileStamperError(msg) elif os.access("%s/%s" % (CFG_WEBSUBMIT_FILE_STAMPER_TEMPLATES_DIR, \ template_name), os.F_OK): ## The template has been found in WebSubmit's latex templates ## directory. Copy it locally to the stamping working directory: try: shutil.copyfile("%s/%s" \ % (CFG_WEBSUBMIT_FILE_STAMPER_TEMPLATES_DIR, \ template_name), \ "%s/%s" % (path_workingdir, template_name)) except IOError: ## Unable to copy the LaTeX template file to the ## working stamping directory: msg = """Error: Unable to copy LaTeX file [%s/%s] to """ \ """working directory for stamping [%s].""" \ % (CFG_WEBSUBMIT_FILE_STAMPER_TEMPLATES_DIR, \ template_name, path_workingdir) raise InvenioWebSubmitFileStamperError(msg) else: ## Now that the template has been found, set the "template ## path" to the WebSubmit latex templates directory: template_path = CFG_WEBSUBMIT_FILE_STAMPER_TEMPLATES_DIR else: ## Unable to locate the latex template. msg = """Error: Unable to locate LaTeX file [%s].""" % template_name raise InvenioWebSubmitFileStamperError(msg) ## Now that the LaTeX template file has been copied locally, extract ## the names of graphics files to be included in the resulting ## document and attempt to copy them to the working "stamp" directory: template_desc = file(os.path.join(path_workingdir, template_name)) template_code = template_desc.read() template_desc.close() graphic_names = [match_obj.group('image') for match_obj in \ re_latex_includegraphics.finditer(template_code)] ## Copy each include-graphic extracted from the template ## into the working stamp directory: for graphic in graphic_names: ## Remove any leading/trailing whitespace: graphic = graphic.strip() ## Get the path and "base name" of the included graphic: (graphic_path, graphic_name) = os.path.split(graphic) ## If there is a graphic_name to work with, try copy the file: if graphic_name != "": if graphic_path != "": ## The graphic is included from an absolute path: if os.access("%s/%s" % (graphic_path, graphic_name), os.F_OK): try: shutil.copyfile("%s/%s" % (graphic_path, \ graphic_name), \ "%s/%s" % (path_workingdir, \ graphic_name)) except IOError: ## Unable to copy the LaTeX template file to ## the current directory msg = """Unable to stamp file. There was """ \ """a problem when trying to copy an image """ \ """[%s/%s] included by the LaTeX template""" \ """ [%s].""" \ % (graphic_path, graphic_name, template_name) raise InvenioWebSubmitFileStamperError(msg) else: msg = """Unable to locate an image [%s/%s] included""" \ """ by the LaTeX template file [%s].""" \ % (graphic_path, graphic_name, template_name) raise InvenioWebSubmitFileStamperError(msg) else: ## The graphic is included from a relative path. Try to obtain ## it from the same directory that the latex template file was ## taken from: if template_path != "": ## Since template path is not empty, try to get the images ## from that location: if os.access("%s/%s" % (template_path, graphic_name), \ os.F_OK): try: shutil.copyfile("%s/%s" % (template_path, \ graphic_name), \ "%s/%s" % (path_workingdir, \ graphic_name)) except IOError: ## Unable to copy the LaTeX template file to ## the current directory msg = """Unable to stamp file. There was """ \ """a problem when trying to copy images """ \ """included by the LaTeX template.""" raise InvenioWebSubmitFileStamperError(msg) else: msg = """Unable to locate an image [%s] included""" \ """ by the LaTeX template file [%s].""" \ % (graphic_name, template_name) raise InvenioWebSubmitFileStamperError(msg) else: ## There is no template path. Try to get the images from ## current dir: if os.access("%s" % graphic_name, os.F_OK): try: shutil.copyfile("%s" % graphic_name, \ "%s/%s" % (path_workingdir, \ graphic_name)) except IOError: ## Unable to copy the LaTeX template file to ## the current directory msg = """Unable to stamp file. There was """ \ """a problem when trying to copy images """ \ """included by the LaTeX template.""" raise InvenioWebSubmitFileStamperError(msg) else: msg = """Unable to locate an image [%s] included""" \ """ by the LaTeX template file [%s].""" \ % (graphic_name, template_name) raise InvenioWebSubmitFileStamperError(msg) ## Return the basename of the template so that it can be used to create ## the PDF stamp file: return template_name def create_final_latex_template(working_dirname, \ latex_template, \ latex_template_var): """In the working directory, create a copy of the the orginal latex template with all the possible xxx--xxx in the template replaced with the values identified by the keywords in the latex_template_var dictionary. @param working_dirname: (string) the working directory used for the creation of the PDF stamp file. @latex_template: (string) name of the latex template before it has been parsed for replacements. @latex_template_var: (dict) dictionnary whose keys are the string to replace in latex_template and values are the replacement content @return: name of the final latex template (after replacements) """ ## Regexp used for finding a substitution line in the original template: re_replacement = re.compile("""XXX-(.+?)-XXX""") ## Now, read-in the local copy of the template and parse it line-by-line, ## replacing any ocurrences of "XXX-SEARCHWORD-XXX" with either: ## ## (a) The value from the "replacements" dictionary; ## (b) Nothing if there was no search-term in the dictionary; try: ## Open the original latex template for reading: fpread = open("%s/%s" \ % (working_dirname, latex_template), "r") ## Open a file to contain the "parsed" latex template: fpwrite = open("%s/create%s" \ % (working_dirname, latex_template), "w") for line in fpread.readlines(): ## For each line in the template file, test for ## substitution-markers: replacement_markers = re_replacement.finditer(line) ## For each replacement-pattern detected in this line, process it: for replacement_marker in replacement_markers: ## Found a match: search_term = replacement_marker.group(1) try: ## Get the replacement-term for this match ## from the dictionary replacement_term = latex_template_var[search_term] except KeyError: ## This search-term was not in the list of replacements ## to be made. It should be replaced with an empty string ## in the template: line = line[0:replacement_marker.start()] + \ line[replacement_marker.end():] else: ## Is the replacement term of the form date(XXXX)? If yes, ## take it literally and generate a pythonic date with it: if replacement_term.find("date(") == 0 \ and replacement_term[-1] == ")": ## Take the date format string, use it to ## generate today's date date_format = replacement_term[5:-1].strip('\'"') try: replacement = time.strftime(date_format, \ time.localtime()) except TypeError: ## Bad date format replacement = "" elif replacement_term.find("include(") == 0 \ and replacement_term[-1] == ")": ## Replacement term is a directive to include a file ## in the LaTeX template: replacement = replacement_term[8:-1].strip('\'"') else: ## Take replacement_term as a literal string ## to be inserted into the template at this point. replacement = replacement_term ## Now substitute replacement into the line of the template: line = line[0:replacement_marker.start()] + replacement \ + line[replacement_marker.end():] ## Write the modified line to the new template: fpwrite.write(line) fpwrite.flush() ## Close up the template files and unlink the original: fpread.close() fpwrite.close() except IOError: msg = "Unable to read LaTeX template [%s/%s]. Cannot Stamp File" \ % (working_dirname, latex_template) raise InvenioWebSubmitFileStamperError(msg) ## Return the name of the LaTeX template to be used: return "create%s" % latex_template def escape_latex_meta_characters(text): """The following are LaTeX meta characters that must be escaped with a backslash: # $ % & _ { } This function therefore takes a string as input and does a simple replace of these characters with escaped versions. @param text: (string) - the string to be escaped. @return: (string) - the string in which the LaTeX meta characters have been escaped. """ text = text.replace('#', '\#') text = text.replace('$', '\$') text = text.replace('%', '\%') text = text.replace('&', '\&') text = text.replace('_', '\_') text = text.replace('{', '\{') text = text.replace('}', '\}') return text def escape_latex_template_vars(template_vars, strict=False): """Take a dictionary of LaTeX template variables/values and escape LaTeX meta characters in some of them, or all of them depending upon whether a call is made in strict mode (if strict is set, ALL values are escaped.) Operating in non-strict mode, the rules for escaping are as follows: * If the string does not contain $ { or }, it must be escaped. * If the string contains $, then there must be an even number of these. If the count is even, do not escape. Else, escape. * If the string contains { or }, it must be balanced with a counterpart. That's to say that the count of "{" must match the count of "}". If it does, do not escape. Else, escape. @param template_vars: (dictionary) - the LaTeX template variables and their values. @param strict: (boolean) - a flag indicating whether or not to operate in strict mode. Strict mode means that all values are escaped regardless of whether or not they are considered to be "good" LaTeX. @return: (dictionary) - the LaTeX template variables with their values escaped. """ ## Make a copy of the LaTeX template variables so as not to corrupt ## the original: working_template_vars = template_vars.copy() ## ## For each of the variables, escape LaTeX meta characteras in the ## value according to the strict flag: varnames = working_template_vars.keys() for varname in varnames: escape_value = False varval = working_template_vars[varname] ## We don't want to escape values that are date or include directives ## so unfortunately, this if is needed here: if (varval.find("date(") == 0 or varval.find("include(") == 0) and \ varval[-1] == ")": ## This is a date or include directive: continue ## Count the number of "$", "{" and "}" in it. If any are present, ## they should be balanced. If so, we will assume that they are ## wanted and that the LaTeX in the string is good. ## If, however, they are not balanced, we will assume that they are ## not valid LaTeX commands and that the string should be escaped. ## If they are not present at all, we assume that the string should ## be escaped. if "$" in varval and varval.count("$") % 2 != 0: ## $ is present, but not in an even number. This string must ## be escaped: escape_value = True elif "{" in varval or "}" in varval: ## "{" and/or "}" is in the value string. Count each of them. ## If they are not matched one to one, consider the string to be ## in need of escaping: if varval.count("{") != varval.count("}"): escape_value = True elif "$" not in varval and "{" not in varval and "}" not in varval: ## Since none of $ { } are in the string, it should be escaped ## to be safe: escape_value = True ## if strict: ## If operating in strict mode, escape everything whatever the ## results of the above tests: escape_value = True ## If the value is to be escaped, go ahead and do so: if escape_value: escaped_varval = escape_latex_meta_characters(varval) working_template_vars[varname] = escaped_varval ## Return the "escaped" LaTeX template variables: return working_template_vars def create_pdf_stamp(path_workingdir, latex_template, latex_template_var): """Retrieve the LaTeX (and associated) files and use them to create a PDF "Stamp" file that can be merged with the main file. The PDF stamp is created in a temporary working directory. @param path_workingdir: (string) the path to the working directory that should be used for creating the PDF stamp file. @param latex_template: (string) - the name of the latex template to be used for the creation of the stamp. @param latex_template_var: (dictionary) - key-value pairs of strings to be sought and replaced within the latex template. @return: (string) - the name of the PDF stamp file. """ ## Copy the LaTeX (and helper) files should be copied into the working dir: template_name = copy_template_files_to_stampdir(path_workingdir, \ latex_template) ## #### ## Make a first attempt at the template PDF creation, escaping the variables ## in non-strict mode: escaped_latex_template_var = escape_latex_template_vars(latex_template_var) ## Now that the latex template and its helper files have been retrieved, ## the Stamp PDF can be created. final_template = create_final_latex_template(path_workingdir, \ template_name, \ escaped_latex_template_var) ## ## The name that will be givem to the PDF stamp file: pdf_stamp_name = "%s.pdf" % os.path.splitext(final_template)[0] ## Now, build the Stamp PDF from the LaTeX template: - cmd_latex = """cd %(workingdir)s; /usr/bin/pdflatex """ \ + cmd_latex = """cd %(workingdir)s; %(path_pdflatex)s """ \ """-interaction=batchmode """ \ """%(template-path)s > /dev/null 2>&1""" \ % { 'template-path' : escape_shell_arg("%s/%s" \ % (path_workingdir, final_template)), 'workingdir' : path_workingdir, + 'path_pdflatex' : CFG_PATH_PDFLATEX, } ## Log the latex command os.system("""echo %s > %s""" % (escape_shell_arg(cmd_latex), \ escape_shell_arg("%s/latex_cmd_first_try" \ % path_workingdir))) ## Run the latex command errcode_latex = os.system("%s" % cmd_latex) ## Was the PDF stamp file successfully created without error? if errcode_latex: ## No it wasn't. Perhaps there was a problem with some of the variable ## values that we substituted into the template? ## To be certain, try to create the PDF one more time - this time ## escaping all of the variable values. ## ## Unlink the PDF file if one was created on the previous attempt: if os.access("%s/%s" % (path_workingdir, pdf_stamp_name), os.F_OK): try: os.unlink("%s/%s" % (path_workingdir, pdf_stamp_name)) except OSError: ## Unable to unlink the PDF file. err_msg = "Unable to unlink the PDF stamp file [%s]. " \ "Stamping has failed." \ % pdf_stamp_name register_exception(prefix=err_msg) raise InvenioWebSubmitFileStamperError(err_msg) ## ## Unlink the LaTeX template file that was created with the previously ## escaped variables: if os.access("%s/%s" % (path_workingdir, final_template), os.F_OK): try: os.unlink("%s/%s" % (path_workingdir, final_template)) except OSError: ## Unable to unlink the LaTeX file. err_msg = "Unable to unlink the LaTeX stamp template file " \ "[%s]. Stamping has failed." \ % final_template register_exception(prefix=err_msg) raise InvenioWebSubmitFileStamperError(err_msg) ## #### ## Make another attempt at the template PDF creation, this time escaping ## the variables in strict mode: escaped_latex_template_var = \ escape_latex_template_vars(latex_template_var, strict=True) ## Now that the latex template and its helper files have been retrieved, ## the Stamp PDF can be created. final_template = create_final_latex_template(path_workingdir, \ template_name, \ escaped_latex_template_var) ## ## The name that will be givem to the PDF stamp file: pdf_stamp_name = "%s.pdf" % os.path.splitext(final_template)[0] ## Now, build the Stamp PDF from the LaTeX template: - cmd_latex = """cd %(workingdir)s; /usr/bin/pdflatex """ \ + cmd_latex = """cd %(workingdir)s; %(path_pdflatex)s """ \ """-interaction=batchmode """ \ """%(template-path)s > /dev/null 2>&1""" \ % { 'template-path' : escape_shell_arg("%s/%s" \ % (path_workingdir, final_template)), 'workingdir' : path_workingdir, + 'path_pdflatex' : CFG_PATH_PDFLATEX, } ## Log the latex command os.system("""echo %s > %s""" \ % (escape_shell_arg(cmd_latex), \ escape_shell_arg("%s/latex_cmd_second_try" \ % path_workingdir))) ## Run the latex command errcode_latex = os.system("%s" % cmd_latex) ## Was the PDF stamp file successfully created? if errcode_latex or \ not os.access("%s/%s" % (path_workingdir, pdf_stamp_name), os.F_OK): ## It was not possible to create the PDF stamp file. Fail. msg = """Error: Unable to create a PDF stamp file.""" raise InvenioWebSubmitFileStamperError(msg) ## Return the name of the PDF stamp file: return pdf_stamp_name ## ***** Functions related to the actual stamping of the file: ***** def apply_stamp_cover_page(path_workingdir, \ stamp_file_name, \ subject_file, \ output_file): """Carry out the stamping: This function adds a cover-page to the file. @param path_workingdir: (string) - the path to the working directory that contains all of the files needed for the stamping process to be carried out. @param stamp_file_name: (string) - the name of the PDF stamp file (i.e. the cover-page itself). @param subject_file: (string) - the name of the file to be stamped. @param output_file: (string) - the name of the final "stamped" file (i.e. that with the cover page added) that will be written in the working directory after the function has ended. """ ## Build the stamping command: cmd_add_cover_page = \ """%(pdftk)s %(cover-page-path)s """ \ """%(file-to-stamp-path)s """ \ """cat output %(stamped-file-path)s """ \ """2>/dev/null"""% \ { 'pdftk' : CFG_PATH_PDFTK, 'cover-page-path' : escape_shell_arg("%s/%s" \ % (path_workingdir, \ stamp_file_name)), 'file-to-stamp-path' : escape_shell_arg("%s/%s" \ % (path_workingdir, \ subject_file)), 'stamped-file-path' : escape_shell_arg("%s/%s" \ % (path_workingdir, \ output_file)), } ## Execute the stamping command: errcode_add_cover_page = os.system(cmd_add_cover_page) ## Was the PDF merged with the coverpage without error? if errcode_add_cover_page: ## There was a problem: msg = "Error: Unable to stamp file [%s/%s]. There was an error when " \ "trying to add the cover page [%s/%s] to the file. Stamping " \ "has failed." \ % (path_workingdir, \ subject_file, \ path_workingdir, \ stamp_file_name) raise InvenioWebSubmitFileStamperError(msg) def apply_stamp_first_page(path_workingdir, \ stamp_file_name, \ subject_file, \ output_file, \ stamp_layer): """Carry out the stamping: This function adds a stamp to the first page of the file. @param path_workingdir: (string) - the path to the working directory that contains all of the files needed for the stamping process to be carried out. @param stamp_file_name: (string) - the name of the PDF stamp file (i.e. the stamp itself). @param subject_file: (string) - the name of the file to be stamped. @param output_file: (string) - the name of the final "stamped" file that will be written in the working directory after the function has ended. @param stamp_layer: (string) - the layer to consider when stamping """ ## Since only the first page of the subject file is to be stamped, ## it's safest to separate this into its own temporary file, stamp ## it, then re-merge it with the remaining pages of the original ## document. In this way, the PDF to be stamped will probably be ## simpler (pages with complex figures and tables will probably be ## avoided) and the process will hopefully have a smaller chance of ## failure. ## ## First of all, separate the first page of the subject file into a ## temporary document: ## ## Name to be given to the first page of the document: output_file_first_page = "p1-%s" % output_file ## Name to be given to the first page of the document once it has ## been stamped: stamped_output_file_first_page = "stamped-%s" % output_file_first_page ## Perform the separation: cmd_get_first_page = \ "%(pdftk)s A=%(file-to-stamp-path)s " \ "cat A1 output %(first-page-path)s " \ "2>/dev/null" \ % { 'pdftk' : CFG_PATH_PDFTK, 'file-to-stamp-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, subject_file)), 'first-page-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ output_file_first_page)), } errcode_get_first_page = os.system(cmd_get_first_page) ## Check that the separation was successful: if errcode_get_first_page or \ not os.access("%s/%s" % (path_workingdir, \ output_file_first_page), os.F_OK): ## Separation was unsuccessful. Fail. msg = "Error: Unable to stamp file [%s/%s] - it wasn't possible to " \ "separate the first page from the rest of the document. " \ "Stamping has failed." \ % (path_workingdir, subject_file) raise InvenioWebSubmitFileStamperError(msg) ## Now stamp the first page: cmd_stamp_first_page = \ "%(pdftk)s %(first-page-path)s %(stamp_layer)s " \ "%(stamp-file-path)s output " \ "%(stamped-first-page-path)s 2>/dev/null" \ % { 'pdftk' : CFG_PATH_PDFTK, 'first-page-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ output_file_first_page)), 'stamp-file-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ stamp_file_name)), 'stamped-first-page-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ stamped_output_file_first_page)), 'stamp_layer' : stamp_layer == 'foreground' and 'stamp' or 'background' } errcode_stamp_first_page = os.system(cmd_stamp_first_page) ## Check that the first page was stamped successfully: if errcode_stamp_first_page or \ not os.access("%s/%s" % (path_workingdir, \ stamped_output_file_first_page), os.F_OK): ## Unable to stamp the first page. Fail. msg = "Error: Unable to stamp the file [%s/%s] - it was not possible " \ "to add the stamp to the first page. Stamping has failed." \ % (path_workingdir, subject_file) raise InvenioWebSubmitFileStamperError(msg) ## Now that the first page has been stamped successfully, merge it with ## the remaining pages of the original file: cmd_merge_stamped_and_original_files = \ "%(pdftk)s A=%(stamped-first-page-path)s " \ "B=%(original-file-path)s cat A1 B2-end output " \ "%(stamped-file-path)s 2>/dev/null" \ % { 'pdftk' : CFG_PATH_PDFTK, 'stamped-first-page-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ stamped_output_file_first_page)), 'original-file-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ subject_file)), 'stamped-file-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ output_file)), } errcode_merge_stamped_and_original_files = \ os.system(cmd_merge_stamped_and_original_files) ## Check to see whether the command exited with an error: if errcode_merge_stamped_and_original_files: ## There was an error when trying to merge the stamped first-page ## with pages 2 onwards of the original file. One possible ## explanation for this could be that the original file only had ## one page (in which case trying to reference pages 2-end would ## cause an error because they don't exist. ## ## Try to get the number of pages in the original PDF. If it only ## has 1 page, the stamped first page file can become the final ## stamped PDF. If it has more than 1 page, there really was an ## error when merging the stamped first page with the rest of the ## pages and stamping can be considered to have failed. cmd_find_number_pages = \ """%(pdftk)s %(original-file-path)s dump_data | """ \ """grep NumberOfPages | """ \ """sed -n 's/^NumberOfPages: \\([0-9]\\{1,\\}\\)$/\\1/p'""" \ % { 'pdftk' : CFG_PATH_PDFTK, 'original-file-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ subject_file)), } fh_find_number_pages = os.popen(cmd_find_number_pages, "r") match_number_pages = fh_find_number_pages.read() errcode_find_number_pages = fh_find_number_pages.close() if errcode_find_number_pages is not None: ## There was an error while checking for the number of pages. ## Fail. msg = "Error: Unable to stamp file [%s/%s]. There was an error " \ "when attempting to merge the file containing the " \ "first page of the stamped file with the remaining " \ "pages of the original file and when an attempt was " \ "made to count the number of pages in the file, an " \ "error was also encountered. Stamping has failed." \ % (path_workingdir, subject_file) raise InvenioWebSubmitFileStamperError(msg) else: try: number_pages_in_subject_file = int(match_number_pages) except ValueError: ## Unable to get the number of pages in the original file. ## Fail. msg = "Error: Unable to stamp file [%s/%s]. There was an " \ "error when attempting to merge the file containing the" \ " first page of the stamped file with the remaining " \ "pages of the original file and when an attempt was " \ "made to count the number of pages in the file, an " \ "error was also encountered. Stamping has failed." \ % (path_workingdir, subject_file) raise InvenioWebSubmitFileStamperError(msg) else: ## Do we have just one page? if number_pages_in_subject_file == 1: ## There was only one page in the subject file. ## copy the version that was stamped on the first page to ## the output_file filename: try: shutil.copyfile("%s/%s" \ % (path_workingdir, \ stamped_output_file_first_page), \ "%s/%s" \ % (path_workingdir, output_file)) except IOError: ## Unable to copy the file that was stamped on page 1 ## Stamping has failed. msg = "Error: It was not possible to copy the " \ "temporary file that was stamped on the " \ "first page [%s/%s] to the final stamped " \ "file [%s/%s]. Stamping has failed." \ % (path_workingdir, \ stamped_output_file_first_page, \ path_workingdir, \ output_file) raise InvenioWebSubmitFileStamperError(msg) else: ## Despite the fact that there was NOT only one page ## in the original file, there was an error when trying ## to merge it with the file that was stamped on the ## first page. Fail. msg = "Error: Unable to stamp file [%s/%s]. There " \ "was an error when attempting to merge the " \ "file containing the first page of the " \ "stamped file with the remaining pages of the " \ "original file. Stamping has failed." \ % (path_workingdir, subject_file) raise InvenioWebSubmitFileStamperError(msg) elif not os.access("%s/%s" % (path_workingdir, output_file), os.F_OK): ## A final version of the stamped file was NOT created even though ## no error signal was encountered during the merging process. ## Fail. msg = "Error: Unable to stamp file [%s/%s]. When attempting to " \ "merge the file containing the first page of the stamped " \ "file with the remaining pages of the original file, no " \ "final file was created. Stamping has failed." \ % (path_workingdir, subject_file) raise InvenioWebSubmitFileStamperError(msg) def apply_stamp_all_pages(path_workingdir, \ stamp_file_name, \ subject_file, \ output_file, \ stamp_layer): """Carry out the stamping: This function adds a stamp to all pages of the file. @param path_workingdir: (string) - the path to the working directory that contains all of the files needed for the stamping process to be carried out. @param stamp_file_name: (string) - the name of the PDF stamp file (i.e. the stamp itself). @param subject_file: (string) - the name of the file to be stamped. @param output_file: (string) - the name of the final "stamped" file that will be written in the working directory after the function has ended. @param stamp_layer: (string) - the layer to consider when stamping """ cmd_stamp_all_pages = \ "%(pdftk)s %(file-to-stamp-path)s %(stamp_layer)s " \ "%(stamp-file-path)s output " \ "%(stamped-file-all-pages-path)s 2>/dev/null" \ % { 'pdftk' : CFG_PATH_PDFTK, 'file-to-stamp-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ subject_file)), 'stamp-file-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ stamp_file_name)), 'stamped-file-all-pages-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ output_file)), 'stamp_layer' : stamp_layer == 'foreground' and 'stamp' or 'background' } errcode_stamp_all_pages = os.system(cmd_stamp_all_pages) if errcode_stamp_all_pages or \ not os.access("%s/%s" % (path_workingdir, output_file), os.F_OK): ## There was a problem stamping the document. Fail. msg = "Error: Unable to stamp file [%s/%s]. Stamping has failed." \ % (path_workingdir, subject_file) raise InvenioWebSubmitFileStamperError(msg) def apply_stamp_to_file(path_workingdir, stamp_type, stamp_file_name, subject_file, output_file, stamp_layer): """Given a stamp-file, the details of the type of stamp to apply, and the details of the file to be stamped, coordinate the process of having that stamp applied to the file. @param path_workingdir: (string) - the path to the working directory that contains all of the files needed for the stamping process to be carried out. @param stamp_type: (string) - the type of stamp to be applied to the file. @param stamp_file_name: (string) - the name of the PDF stamp file (i.e. the stamp itself). @param subject_file: (string) - the name of the file to be stamped. @param output_file: (string) - the name of the final "stamped" file that will be written in the working directory after the function has ended. @param stamp_layer: (string) - the layer to consider when stamping the file. @return: (string) - the name of the stamped file that has been created. It will be found in the stamping working directory. """ ## Stamping is performed on PDF files. We therefore need to test for the ## type of the subject file before attempting to stamp it: ## ## Initialize a variable to hold the "file type" of the subject file: subject_filetype = "" ## Using the file command, test for the file-type of "subject_file": cmd_gfile = "%(gfile)s %(file-to-stamp-path)s 2> /dev/null" \ % { 'gfile' : CFG_PATH_GFILE, 'file-to-stamp-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ subject_file)), } ## Execute the file command: fh_gfile = os.popen(cmd_gfile, "r") ## Read the results string output by gfile: output_gfile = fh_gfile.read() ## Close the pipe and capture its error code: errcode_gfile = fh_gfile.close() ## If a result was obtained from gfile, scan it for an acceptable file-type: if errcode_gfile is None and output_gfile != "": output_gfile = output_gfile.lower() if "pdf document" in output_gfile: ## This is a PDF file. subject_filetype = "pdf" elif "postscript" in output_gfile: ## This is a PostScript file. subject_filetype = "ps" ## Unable to determine the file type using gfile. ## Try to determine the file type by examining its extension: if subject_filetype == "": ## split the name of the file to be stamped on "." and take the last ## part of it. This should be the "extension". tmp_file_extension = subject_file.split(".")[-1] if tmp_file_extension.lower() == "pdf": subject_filetype = "pdf" elif tmp_file_extension.lower() == "ps": subject_filetype = "ps" if subject_filetype not in ("ps", "pdf"): ## unable to process file. msg = """Error: Input file [%s] is not PDF or PS. - unable to """ \ """perform stamping.""" % subject_file raise InvenioWebSubmitFileStamperError(msg) if subject_filetype == "ps": ## Convert the subject file from PostScript to PDF: if subject_file[-3:].lower() == ".ps": ## The name of the file to be stamped has a PostScript extension. ## Strip it and give the name of the PDF file to be created a ## PDF extension: created_pdfname = "%s.pdf" % subject_file[:-3] elif len(subject_file.split(".")) > 1: ## The file name has an extension - strip it and add a PDF ## extension: raw_name = subject_file[:subject_file.rfind(".")] if raw_name != "": created_pdfname = "%s.pdf" % raw_name else: ## It would appear that the file had no extension and that its ## name started with a period. Just use the original name with ## a .pdf suffix: created_pdfname = "%s.pdf" % subject_file else: ## No extension - use the original name with a .pdf suffix: created_pdfname = "%s.pdf" % subject_file ## Build the distilling command: cmd_distill = """%(distiller)s %(ps-file-path)s """ \ """%(pdf-file-path)s 2>/dev/null""" % \ { 'distiller' : CFG_PATH_PS2PDF, 'ps-file-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ subject_file)), 'pdf-file-path' : escape_shell_arg("%s/%s" % \ (path_workingdir, \ created_pdfname)), } ## Distill the PS into a PDF: errcode_distill = os.system(cmd_distill) ## Test to see whether the PS was distilled into a PDF without error: if errcode_distill or \ not os.access("%s/%s" % (path_workingdir, created_pdfname), os.F_OK): ## The PDF file was not correctly created in the working directory. ## Unable to continue with the stamping process. msg = "Error: Unable to correctly convert PostScript file [%s] to" \ " PDF. Cannot stamp file." % subject_file raise InvenioWebSubmitFileStamperError(msg) ## Now assign the name of the created PDF file to subject_file: subject_file = created_pdfname ## Treat the name of "output_file": if output_file in (None, ""): ## there is no value for outputfile. outfile should be given the same ## name as subject_file, but with "stamped-" appended to the front. ## E.g.: subject_file: test.pdf; outfile: stamped-test.pdf output_file = "stamped-%s" % subject_file else: ## If output_file has an extension, strip it and add a PDF extension: if len(output_file.split(".")) > 1: ## The file name has an extension - strip it and add a PDF ## extension: raw_name = output_file[:output_file.rfind(".")] if raw_name != "": output_file = "%s.pdf" % raw_name else: ## It would appear that the file had no extension and that its ## name started with a period. Just use the original name with ## a .pdf suffix: output_file = "%s.pdf" % output_file else: ## No extension - use the original name with a .pdf suffix: output_file = "%s.pdf" % output_file if stamp_type == 'coverpage': ## The stamp to be applied to the document is in fact a "cover page". ## This means that the entire PDF "stamp" that was created from the ## LaTeX template is to be appended to the subject file as the first ## page (i.e. a cover-page). apply_stamp_cover_page(path_workingdir, \ stamp_file_name, \ subject_file, \ output_file) elif stamp_type == "first": apply_stamp_first_page(path_workingdir, \ stamp_file_name, \ subject_file, \ output_file, \ stamp_layer) elif stamp_type == 'all': ## The stamp to be applied to the document is a simple that that should ## be applied to ALL pages of the document (i.e. merged onto each page.) apply_stamp_all_pages(path_workingdir, \ stamp_file_name, \ subject_file, \ output_file, \ stamp_layer) else: ## Unexpcted stamping mode. msg = """Error: Unexpected stamping mode [%s]. Stamping has failed.""" \ % stamp_type raise InvenioWebSubmitFileStamperError(msg) ## Finally, if the original subject file was a PS, convert the stamped ## PDF back to PS: if subject_filetype == "ps": if output_file[-4:].lower() == ".pdf": ## The name of the file to be stamped has a PDF extension. ## Strip it and give the name of the PDF file to be created a ## PDF extension: stamped_psname = "%s.ps" % output_file[:-4] elif len(output_file.split(".")) > 1: ## The file name has an extension - strip it and add a PDF ## extension: raw_name = output_file[:output_file.rfind(".")] if raw_name != "": stamped_psname = "%s.ps" % raw_name else: ## It would appear that the file had no extension and that its ## name started with a period. Just use the original name with ## a .pdf suffix: stamped_psname = "%s.ps" % output_file else: ## No extension - use the original name with a .pdf suffix: stamped_psname = "%s.ps" % output_file ## Build the conversion command: cmd_pdf2ps = "%s %s %s 2>/dev/null" % (CFG_PATH_PDF2PS, escape_shell_arg("%s/%s" % \ (path_workingdir, \ output_file)), escape_shell_arg("%s/%s" % \ (path_workingdir, \ stamped_psname))) errcode_pdf2ps = os.system(cmd_pdf2ps) ## Check to see that the command executed OK: if not errcode_pdf2ps and \ os.access("%s/%s" % (path_workingdir, stamped_psname), os.F_OK): ## No problem converting the PDF to PS. output_file = stamped_psname ## Return the name of the "stamped" file: return output_file def copy_subject_file_to_working_directory(path_workingdir, input_file): """Attempt to copy the subject file (that which is to be stamped) to the current working directory, returning the name of the subject file if successful. @param path_workingdir: (string) - the path to the working directory for the current stamping session. @param input_file: (string) - the path to the subject file (that which is to be stamped). @return: (string) - the name of the subject file, which has been copied to the current working directory. @Exceptions raised: (InvenioWebSubmitFileStamperError) - upon failure to successfully copy the subject file to the working directory. """ ## Divide the input filename into path and basename: (dummy, name_input_file) = os.path.split(input_file) if name_input_file == "": ## The input file is just a path - not a valid filename. Fail. msg = """Error: unable to determine the name of the file to be """ \ """stamped.""" raise InvenioWebSubmitFileStamperError(msg) ## Test to see whether the stamping subject file is a real file and ## is readable: if os.access("%s" % input_file, os.R_OK): ## File is readable. Copy it locally to the working directory: try: shutil.copyfile("%s" % input_file, \ "%s/%s" % (path_workingdir, name_input_file)) except IOError: ## Unable to copy the stamping subject file to the ## working directory. Fail. msg = """Error: Unable to copy stamping file [%s] to """ \ """working directory for stamping [%s].""" \ % (input_file, path_workingdir) raise InvenioWebSubmitFileStamperError(msg) else: ## Unable to read the subject file. Fail. msg = """Error: Unable to copy stamping file [%s] to """ \ """working directory [%s]. (File not readable.)""" \ % (input_file, path_workingdir) raise InvenioWebSubmitFileStamperError(msg) ## Now that the stamping file has been successfully copied to the working ## directory, return its base name: return name_input_file def create_working_directory(): """Create a "working directory" in which the files related to the stamping process can be stored, and return the full path to it. The working directory will be created in ~invenio/var/tmp. If it cannot be created there, an exception (InvenioWebSubmitFileStamperError) will be raised. The working directory will have the prefix "websubmit_file_stamper_", and could be given a name something like: - websubmit_file_stamper_Tzs3St @return: (string) - the full path to the working directory. @Exceptions raised: InvenioWebSubmitFileStamperError. """ ## Create the temporary directory in which to place the LaTeX template ## and its helper files in ~invenio/var/tmp: path_workingdir = None try: path_workingdir = tempfile.mkdtemp(prefix="websubmit_file_stamper_", \ dir="%s" % CFG_TMPDIR) except OSError, err: ## Unable to create the temporary directory in ~invenio/var/tmp msg = "Error: Unable to create a temporary working directory in " \ "which to carry out the stamping process. An attempt was made " \ "to create the directory in [%s]; the error encountered was " \ "<%s>. Stamping has failed." % (CFG_TMPDIR, str(err)) raise InvenioWebSubmitFileStamperError(msg) ## return the path to the working-directory: return path_workingdir ## ***** Functions Specific to CLI calling of the program: ***** def usage(wmsg="", err_code=0): """Print a "usage" message (along with an optional additional warning/error message) to stderr and exit with a given error code. @param wmsg: (string) - some kind of warning message for the user. @param err_code: (integer) - an error code to be passed to sys.exit, which is called after the usage message has been printed. @return: None. """ ## Wash the warning message: if wmsg != "": wmsg = wmsg.strip() + "\n" ## The usage message: msg = """ Usage: python ~invenio/lib/python/invenio/websubmit_file_stamper.py \\ [options] input-file.pdf websubmit_file_stamper.py is used to add a "stamp" to a PDF file. A LaTeX template is used to create the stamp and this stamp is then concatenated with the original PDF file. The stamp can take the form of either a separate "cover page" that is appended to the document; or a "mark" that is applied somewhere either on the document's first page or on all of its pages. Options: -h, --help Print this help. -V, --version Print version information. -v, --verbose=LEVEL Verbose level (0=min, 1=default, 9=max). [NOT IMPLEMENTED] -t, --latex-template=PATH Path to the LaTeX template file that should be used for the creation of the PDF stamp. (Note, if it's just a basename, it will be sought first in the current working directory, and then in the invenio file-stamper templates directory; If there is a qualifying path to the template name, it will be sought only in that location); -c, --latex-template-var='VARNAME=VALUE' A variable that should be replaced in the LaTeX template file with its corresponding value. Of the following format: VARNAME=VALUE This option is repeatable - one for each template variable; -s, --stamp=STAMP-TYPE The type of stamp to be applied to the subject file. Must be one of 3 values: + "first" - stamp only the first page; + "all" - stamp all pages; + "coverpage" - add a cover page to the document; The default value is "first"; -l, --layer=LAYER The position of the stamp. Should be one of: + "background" (invisible if original file has a white -not transparent- background layer) + "foreground" (on top of the stamped file. If the stamp does not have a transparent background, will hide all of the document layers) The default value is "background" -o, --output-file=XYZ The optional name to be given to the finished (stamped) file IN THE WORKING DIRECTORY (Specify a file name, including extension, not a path). If this is omitted, the stamped file will be given the same name as the input file, but will be prefixed by"stamped-"; Example: python ~invenio/lib/python/invenio/websubmit_file_stamper.py \\ --latex-template=demo-stamp-left.tex \\ --latex-template-var='REPORTNUMBER=TEST-THESIS-2008-019' \\ --latex-template-var='DATE=27/02/2008' \\ --stamp='first' \\ --layer='background' \\ --output-file=testfile_stamped.pdf \\ testfile.pdf """ sys.stderr.write(wmsg + msg) sys.exit(err_code) def get_cli_options(): """From the options and arguments supplied by the user via the CLI, build a dictionary of options to drive websubmit-file-stamper. For reference, the CLI options available to the user are as follows: -h, --help -> Display help/usage message and exit; -V, --version -> Display version information and exit; -v, --verbose= -> Set verbosity level (0=min, 1=default, 9=max). -t, --latex-template= -> Path to the LaTeX template file that should be used for the creation of the PDF stamp. (Note, if it's just a basename, it will be sought first in the current working directory, and then in the invenio file-stamper templates directory; If there is a qualifying path to the template name, it will be sought only in that location); -c, --latex-template-var= -> A variable that should be replaced in the LaTeX template file with its corresponding value. Of the following format: varname=value This option is repeatable - one for each template variable; -s, --stamp= The type of stamp to be applied to the subject file. Must be one of 3 values: + "first" - stamp only the first page; + "all" - stamp all pages; + "coverpage" - add a cover page to the document; The default value is "first"; -l, --layer= -> The position of the stamp. Should be one of: + "background" (invisible if original file has a white -not transparent- background layer) + "foreground" (on top of the stamped file. If the stamp does not have a transparent background, will hide all of the document layers). The default value is "background" -o, --output-file= -> The optional name to be given to the finished (stamped) file IN THE WORKING DIRECTORY (Specify a name, not a path). If this is omitted, the stamped file will be given the same name as the input file, but will be prefixed by"stamped-"; @return: (dictionary) of input options and flags, set as appropriate. The dictionary has the following structure: + latex-template: (string) - the path to the LaTeX template to be used for the creation of the stamp itself; + latex-template-var: (dictionary) - This dictionary contains variables that should be sought in the LaTeX template file, and the values that should be substituted in their place. E.g.: { "TITLE" : "An Introduction to CDS Invenio" } + input-file: (string) - the path to the input file (i.e. that which is to be stamped; + output-file: (string) - the name of the stamped file that should be created by the program. This is optional - if not provided, a default name will be applied to a file instead; + stamp: (string) - the type of stamp that is to be applied to the input file. It must take one of 3 values: - "first": Stamp only the first page of the document; - "all": Apply the stamp to all pages of the document; - "coverpage": Add a "cover page" to the document; + layer: (string) - the position of the stamp in the layers of the file. Will be one of the following values: - "background": stamp applied to the background layer; - "foreground": stamp applied to the foreground layer; + verbosity: (integer) - the verbosity level under which the program is to run; So, an example of the returned dictionary would be something like: { 'latex-template' : "demo-stamp-left.tex", 'latex-template-var' : { "REPORTNUMBER" : "TEST-2008-001", "DATE" : "15/02/2008", }, 'input-file' : "test-doc.pdf", 'output-file' : "", 'stamp' : "first", 'layer' : "background", 'verbosity' : 0, } """ ## dictionary of important values relating to cli call of program: options = { 'latex-template' : "", 'latex-template-var' : {}, 'input-file' : "", 'output-file' : "", 'stamp' : "first", 'layer' : "background", 'verbosity' : 0, } ## Get the options and arguments provided by the user via the CLI: try: myoptions, myargs = getopt.getopt(sys.argv[1:], "hVv:t:c:s:l:o:", \ ["help", "version", "verbosity=", "latex-template=", "latex-template-var=", "stamp=", "layer=", "output-file="]) except getopt.GetoptError, err: ## Invalid option provided - usage message usage(wmsg="Error: %(msg)s." % { 'msg' : str(err) }) ## Get the input file from the arguments list (it should be the ## first argument): if len(myargs) > 0: options["input-file"] = myargs[0] ## Extract the details of the options: for opt in myoptions: if opt[0] in ("-V","--version"): ## version message and exit sys.stdout.write("%s\n" % __revision__) sys.stdout.flush() sys.exit(0) elif opt[0] in ("-h","--help"): ## help message and exit usage() elif opt[0] in ("-v", "--verbosity"): ## Get verbosity level: if not opt[1].isdigit(): options['verbosity'] = 0 elif int(opt[1]) not in xrange(0, 10): options['verbosity'] = 0 else: options['verbosity'] = int(opt[1]) elif opt[0] in ("-o", "--output-file"): ## Get the name of the "output file" that is to be created after ## stamping (i.e. the "stamped file"): options["output-file"] = opt[1] if '/' in options["output-file"]: # probably user specified a file path, which is not # supported print "Warning: you seem to have specifed a path for option '--output-file'." print "Only a file name can be specified. Stamping might fail." elif opt[0] in ("-t", "--latex-template"): ## Get the path to the latex template to be used for the creation ## of the stamp file: options["latex-template"] = opt[1] elif opt[0] in ("-m", "--stamp"): ## The type of stamp that is to be applied to the document: ## Options are coverpage, first, all: if str(opt[1].lower()) in ("coverpage", "first", "all"): ## Valid stamp type, accept it; options["stamp"] = str(opt[1]).lower() else: ## Invalid stamp type. Print usage message and quit. usage(wmsg="Chosen stamp type '%s' is not valid" % opt[1]) elif opt[0] in ("-l", "--layer"): ## The layer to consider for the stamp if str(opt[1].lower()) in ("background", "foreground"): ## Valid layer type, accept it; options["layer"] = str(opt[1]).lower() else: ## Invalid layer type. Print usage message and quit. usage(wmsg="Chosen layer type '%s' is not valid" % opt[1]) elif opt[0] in ("-c", "--latex-template-var"): ## This is a variable to be replaced in the LaTeX template. ## It should take the following form: ## varname=value ## We can therefore split it on the first "=" sign - anything to ## left will be considered to be the name of the variable to search ## for; anything to the right will be considered as the value that ## should replace the variable in the LaTeX template. ## Note: If the user supplies the same variable name more than once, ## the latter occurrence will be kept and the previous value will be ## overwritten. ## Note also that if the variable string does not take the ## expected format a=b, it will be ignored. ## ## Get the complete string: varstring = str(opt[1]) ## Split into 2 string based on the first "=": split_varstring = varstring.split("=", 1) if len(split_varstring) == 2: ## Split based on equals sign was successful: if split_varstring[0] != "": ## The variable name was not empty - keep it: options["latex-template-var"]["%s" % split_varstring[0]] = \ "%s" % split_varstring[1] ## Return the input options: return options def stamp_file(options): """The driver for the stamping process. This is effectively the function that is responsible for coordinating the stamping of a file. @param options: (dictionary) - a dictionary of options that are required by the function in order to carry out the stamping process. The dictionary must have the following structure: + latex-template: (string) - the path to the LaTeX template to be used for the creation of the stamp itself; + latex-template-var: (dictionary) - This dictionary contains variables that should be sought in the LaTeX template file, and the values that should be substituted in their place. E.g.: { "TITLE" : "An Introduction to CDS Invenio" } + input-file: (string) - the path to the input file (i.e. that which is to be stamped; + output-file: (string) - the name of the stamped file that should be created by the program IN THE WORKING DIRECTORY (Specify a name, not a path). This is optional - if not provided, a default name will be applied to a file instead; + stamp: (string) - the type of stamp that is to be applied to the input file. It must take one of 3 values: - "first": Stamp only the first page of the document; - "all": Apply the stamp to all pages of the document; - "coverpage": Add a "cover page" to the document; + layer: (string) - the layer to consider to stamp the file. Can be one of the following values: - "background": stamp the background layer; - "foreground": stamp the foreground layer; + verbosity: (integer) - the verbosity level under which the program is to run; So, an example of the returned dictionary would be something like: { 'latex-template' : "demo-stamp-left.tex", 'latex-template-var' : { "REPORTNUMBER" : "TEST-2008-001", "DATE" : "15/02/2008", }, 'input-file' : "test-doc.pdf", 'output-file' : "", 'stamp' : "first", 'layer' : "background" 'verbosity' : 0, } @return: (tuple) - consisting of two strings: 1. the path to the working directory in which all stamping-related files are stored; 2. The name of the "stamped" file; @Exceptions raised: (InvenioWebSubmitFileStamperError) exceptions may be raised or propagated by this function when the stamping process fails for one reason or another. """ ## SANITY CHECKS: ## Does the options dictionary contain all mandatory keys? ## ## A list of the names of the expected options: mandatory_option_names = ["latex-template", \ "latex-template-var", \ "input-file", \ "output-file"] optional_option_names_and_defaults = {"layer": "background", \ "verbosity": 0, "stamp": "first"} ## Are we missing some mandatory parameters? received_option_names = options.keys() for mandatory_option_name in mandatory_option_names: if not mandatory_option_name in received_option_names: msg = """Error: Mandatory parameter %s is missing""" % mandatory_option_name raise InvenioWebSubmitFileStamperError(msg) ## Are we getting some unknown option? for received_option_name in received_option_names: if not received_option_name in mandatory_option_names and \ not received_option_name in optional_option_names_and_defaults.keys(): ## Error: the dictionary of options had an illegal structure: msg = """Error: Option %s is not a recognized parameter""" % received_option_name raise InvenioWebSubmitFileStamperError(msg) ## Set default options when not specified for opt, value in optional_option_names_and_defaults.iteritems(): if not options.has_key(opt): options[opt] = value ## Do we have an input file to work on? if options["input-file"] in (None, ""): ## No input file - stop the stamping: msg = "Error: unable to determine the name of the file to be stamped." raise InvenioWebSubmitFileStamperError(msg) ## Do we have a LaTeX file for creation of the stamp? if options["latex-template"] in (None, ""): ## No latex stamp file - stop the stamping: msg = "Error: unable to determine the name of the LaTeX template " \ "file to be used for stamp creation." raise InvenioWebSubmitFileStamperError(msg) ## OK - begin the document stamping process: ## ## Get the output file: (dummy, name_outfile) = os.path.split(options["output-file"]) if name_outfile != "": ## Take just the basename component of outfile: options["output-file"] = name_outfile ## Create a working directory (in which to store the various files used and ## created during the stamping process) and get the full path to it: path_workingdir = create_working_directory() ## Copy the file to be stamped into the working directory: basename_input_file = \ copy_subject_file_to_working_directory(path_workingdir, \ options["input-file"]) ## Now import the LaTeX (and associated) files into a temporary directory ## and use them to create the "stamp" PDF: pdf_stamp_name = create_pdf_stamp(path_workingdir, \ options["latex-template"], \ options["latex-template-var"]) ## Everything is now ready to merge the "stamping subject" file with the ## PDF "stamp" file that has been created: name_stamped_file = apply_stamp_to_file(path_workingdir, \ options["stamp"], \ pdf_stamp_name, \ basename_input_file, \ options["output-file"], \ options["layer"]) ## Return a tuple containing the working directory and the name of the ## stamped file to the caller: return (path_workingdir, name_stamped_file) def stamp_file_cli(): """The function responsible for triggering the stamping process when called via the CLI. This function will effectively get the CLI options, then pass them to function that is responsible for coordinating the stamping process itself. Once stamping has been completed, an attempt will be made to copy the stamped file to the current working directory. """ ## Get CLI options and arguments: input_options = get_cli_options() ## Stamp the file and obtain the working directory in which the stamped file ## is situated and the name of the stamped file: try: (working_dir, stamped_file) = stamp_file(input_options) except InvenioWebSubmitFileStamperError, err: ## Something went wrong: sys.stderr.write("Stamping failed: [%s]\n" % str(err)) sys.stderr.flush() sys.exit(1) if not os.access("./%s" % stamped_file, os.F_OK): ## Copy the stamped file into the current directory: try: shutil.copyfile("%s/%s" % (working_dir, stamped_file), \ "./%s" % stamped_file) except IOError: ## Report that it wasn't possible to copy the stamped file locally ## and offer the user a path to it: msg = "It was not possible to copy the stamped file to the " \ "current working directory.\nYou can find it here: " \ "[%s/%s].\n" \ % (working_dir, stamped_file) sys.stderr.write(msg) sys.stderr.flush() else: ## A file exists in curdir with the same name as the final stamped file. ## just print out a message stating this fact, along with the path to ## the stamped file in the temporary working directory: msg = "The stamped file [%s] has not been copied to the current " \ "working directory because a file with this name already " \ "existed there.\nYou can find the stamped file here: " \ "[%s/%s].\n" % (stamped_file, working_dir, stamped_file) sys.stderr.write(msg) sys.stderr.flush() ## Start proceedings for CLI calls: if __name__ == "__main__": stamp_file_cli()