diff --git a/docs/admin/index.rst b/docs/admin/index.rst index f70244274..267852a86 100644 --- a/docs/admin/index.rst +++ b/docs/admin/index.rst @@ -1,47 +1,46 @@ .. This file is part of Invenio Copyright (C) 2014, 2015 CERN. Invenio is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Invenio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Invenio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. .. _admins-guide: ============= Admin's Guide ============= .. toctree:: :maxdepth: 1 bibauthority-admin-guide bibcheck-admin-guide bibclassify-admin-guide bibconvert-admin-guide bibedit-admin-guide bibindex-admin-guide bibknowledge-admin-guide bibmatch-admin-guide bibrank-admin-guide bibsched-admin-guide bibsort-admin-guide bibupload-admin-guide docextract-admin-guide oaiharvest-admin-guide oairepository-admin-guide publiline-admin-guide - webcomment-admin-guide webmessage-admin-guide websearch-admin-guide websession-admin-guide webstyle-admin-guide diff --git a/docs/admin/webcomment-admin-guide.rst b/docs/admin/webcomment-admin-guide.rst deleted file mode 100644 index ca292231c..000000000 --- a/docs/admin/webcomment-admin-guide.rst +++ /dev/null @@ -1,340 +0,0 @@ -.. This file is part of Invenio - Copyright (C) 2014 CERN. - - Invenio is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - Invenio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Invenio; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -.. _webcomment-admin-guide: - -WebComment Admin Guide -====================== - -1. Overview ------------ - -WebComment manages all aspects related to comments. From the admin -interface it is possible to check comment statistics as well as manage -comments reported. If the user is authorized for moderation, when -viewing a record special links are displayed to execute actions such as -delete/undelete or unreport comments. - -2. Managing WebComment ----------------------- - -2.1 Viewing comments/review information -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -From the admin interface it is possible to view statistics related to -the most commented/reviewed records and latest comments/reviews posted. - -Depending on the role the user has, it will be possible to view -information related to the collections the user is authorized to. - -3. Configuring WebComment -------------------------- - -3.1 Configuring moderator per collection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Configuration to specify which user is moderator of a given collection -is done through WebAccess administration interface. - -In order to permit a given user to be a comment moderator, the following -steps have to be followed: - -- Create a role for the user who is going to moderate (one for each - moderator) -- Go to the Action Administration Menu inside of WebAccess admin - interface - and assign this role to the action 'moderatecomments'. Specify as - argument - the collections allowed for the user. -- In case you want to give the superadmin user access to all - collections, follow - the steps above and write \* as the collection argument. - -3.2 Enabling LaTeX/MathJax in comments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It is possible to enable LaTeX rendering in comments with the MathJax -module. In order to do that, it is necessary to set the following -directive in the invenio configuration file: - -:: - - CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS = 1 - -3.3 Configuring threading -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Replies to comments can be organized in threads. It is possible to -configure the maximum depth of the threads with the -``CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH`` variable in the -``invenio-local.conf`` file. - -Set ``CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = -1`` to not limit the -depth. - -Set ``CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = 0`` for a "flat" -discussion. - -Set ``CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = 1`` for one-level -deep threads (**default**) etc.. - -The main advantage of setting a maximum depth is to not enter into deep, -meaningless indentations when users might not understand the concept of -threads, and mix the "reply to" and "add comment" actions. When -``CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH`` is reached, the level of -discussion becomes a "flat" discussion. - -For example, with ``CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = 1``, -replies to comments would go through to the following states: - -State 1 - -State 2 - -State 3 - -State 4 - -Comment 1 - -Reply to - -Comment 2 - -Reply to - -:: - - Reply to - Comment 1 - --------> - -Comment 1 - -Reply to - -**Comment 3** - -Reply to - -Comment 2 - -Reply to - -:: - - Reply to - Comment 3 - --------> - -Comment 1 - -Reply to - -Comment 3 - -Reply to - -**Comment 4** - -Reply to - -Comment 2 - -Reply to - -:: - - Reply to - Comment 3 - --------> - -Comment 1 - -Reply to - -Comment 3 - -Reply to - -Comment 4 - -Reply to - -**Comment 5** - -Reply to - -Comment 2 - -Reply to - -3.4 Configuring commenting rounds -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can configure WebComment to group comments according to the status -of the commented record. This enables the creation of commenting -*rounds*, so that comments belonging to the same round are grouped -together, and comments belonging to past rounds are less prominently -displayed. - -For example, let's say that a collaboration is commenting a draft -document: once a new revision of the document is uploaded by the author, -the already existing comments are grouped together and moved to archive. -The discussion page will then appear empty, and a new commenting round -can start. - -The past commenting rounds are still visible as small individual -links at the top of the discussion. A click on one of these links -reveals the comments belonging to this group/round. It is then also -possible to continue replying to comments submitted during this past -round, though it is not possible to add a new comment (not a reply). - -:: - - > Comments for revision 1 - > Comments for revision 2 - v Comments for revision 3 - -Great paper. Check typo line 12. - -Reply to - -Are you sure that figure 5 is ok? - -Reply to - -Please include the standard logo. - -Reply to - -:: - - New revision - of file - ------------> - - >Comments for revision 1 - >Comments for revision 2 - > Comments for revision 3 - v Comments for revision 4 - - -*(blank)* - -In order to know what is the current round, the MARC metadata of the -commented record must specify the name of the current group to which new -comments have to be attached. The MARC field specifying the round "name" -can be configured with the ``CFG_WEBCOMMENT_ROUND_DATAFIELD`` of the -``invenio-local.conf`` file. There you can set the MARC code that -contains the round name, for each collection you want to enable rounds. - -Note that whatever the current round is, a reply to a comment will -always belong to the round of the parent. - -*How to modify the MARC field with the round name?*: this is up to the -admin of the system, but it would typically be done by a WebSubmit -submission. This field might already exists if you store a revision -number in your metadata, or date, etc. - -*How to restrict commenting rounds?*: you can combine rounds with the -comment-level restrictions documented in section `Comment-level -restrictions <#3.5.3>`__, which uses a similar mechanism to protect -comments. - -3.5 Configuring restrictions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Comments restrictions can be configured at several levels, detailed in -the next sections. Note that restrictions applied to comments also apply -to the files attached to comments. - -3.5.1 Record-level restrictions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When a record is restricted via its collection (``viewrestrcoll`` -WebAccess action), the comments submitted for this record are restricted -to users who can view the record. - -3.5.2 Discussion-level restrictions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Discussion pages can be restricted with the WebAccess ``viewcomment`` -and ``sendcomment`` actions. This let you define who can read or send -comments in particular collections. - -3.5.3 Comment-level restrictions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Restrictions can be applied at the level of individual records. When -submitting a comment, WebComment checks in the record metadata if some -restriction should be applied. The field where WebComment is looking for -restrictions can be configured in the the -``CFG_WEBCOMMENT_RESTRICTION_DATAFIELD`` of the ``invenio-local.conf`` -file. There you can define for each collection the MARC code containing -the restriction information. - -For the restriction to be applied, an authorisation for the -``viewrestrcomment`` WebAccess action must be set up. The "status" -parameter of the ``viewrestrcomment`` must match the value in the MARC -metadata. This lets you define different restrictions based on the value -in the metadata. - -Unless the status is empty, the comment will be restricted, even if no -role is specifically linked to the ``viewrestrcomment`` action for this -status. - -Note that whatever the status of the record is, a reply to a comment -always inherits the restriction of the parent. - -*How to modify the MARC field with the restriction?*: this is up to the -admin of the system, but it would typically be done by a WebSubmit -submission. - -3.6 Configuring file attachments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WebComment allows authorized users to attach files to submitted -comments. This is only available to logged in users, who have been -authorized via the ``attachcommentfile`` WebAccess action. By default no -user (except admin) can attach files. - -In addition, you can configure the maximum number of allowed -attachments with the ``CFG_WEBCOMMENT_MAX_ATTACHED_FILES`` (default 5) -variable of the the ``invenio-local.conf`` file. Set -``CFG_WEBCOMMENT_MAX_ATTACHED_FILES = 0`` for unlimited number of files. - -Note that this is only applicable only if you installed the jQuery -plugins with ``make install-jquery-plugins``. - -You can set up the maximum size (in bytes) of attachments with the -``CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE`` variable of the the -``invenio-local.conf`` file. - -3.7 Configuring email notifications -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WebComment allows users to subscribe to discussions directly from the -web interface. In addition you can set up automatic notifications that -don't require the users to subscribe. The main use case for this is to -notify the author that a new comment has been submitted on her document. - -WebComment checks for automatic notifications of comments in the MARC -record. It specifically looks for emails in fields defined by the -``CFG_WEBCOMMENT_EMAIL_REPLIES_TO`` variable of the the -``invenio-local.conf`` file. There you can define the fields to look for -for specific collections. diff --git a/docs/admin/webstyle-admin-guide.rst b/docs/admin/webstyle-admin-guide.rst index ac58ee1ea..007861987 100644 --- a/docs/admin/webstyle-admin-guide.rst +++ b/docs/admin/webstyle-admin-guide.rst @@ -1,515 +1,504 @@ .. This file is part of Invenio Copyright (C) 2014 CERN. Invenio is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Invenio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Invenio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. .. _webstyle-admin-guide: WebStyle Admin Guide ==================== Contents -------- - **1. `Overview <#overview_page_layout>`__** - 1.1 \ `CSS Style Sheet and Images <#overview_css>`__ - 1.2 \ `HTML Page Layout <#overview_page_layout>`__ - 1.2.1 \ `Layout of HTML Static Pages <#overview_page_layout_stat>`__ - 1.2.2 \ `Layout of Python Dynamic Pages <#overview_page_layout_dyn>`__ - 1.3 \ `Look of Bibliographic References <#overview_bib>`__ - 1.4 \ `Specific Configurations <#overview_spec_conf>`__ - **2. `Detailed Record Pages <#det_page>`__** - 2.1 \ `Available tabs <#det_page>`__ - 2.2 \ `Showing/Hiding tabs <#det_show_hide_tabs>`__ - 2.3 \ `Customizing content of tabs <#det_page_cust_cont_tabs>`__ - 2.4 \ `Customizing look of tabs <#det_page_cust_look_tabs>`__ - **3. `Custom Redirections <#red>`__** - 3.1 \ `Command Line Interface <#red_cli>`__ 1. Overview ----------- This document describes how to change the look and feel of your CDS Invenio installation. 1.1 CSS Style Sheet and Images ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The most obvious modification you may want to do is the modification of `CSS style sheet </img/invenio.css>`__. You may also customize default `images </img/>`__. 1.2 HTML Page Layout ~~~~~~~~~~~~~~~~~~~~ The customization of the general page look and feel is currently different depending on whether you customize HTML-like static pages or dynamic Python pages. Dynamic HTML pages are used to build the 'interactive' parts of the website, such as the search and browse pages, as well as the admin pages. The content of these pages is defined at run time, depending on the users parameters. You can modify them to provide a totally different experience to your users when browsing Atlantis Institute of Fictive Science. Most probably, you will only want to customize 'webstyle\_templates.py', which define the headers and footer of a page. Static HTML pages are used for basic pages that do not embed dynamic content, such as this guide. They help reducing the load of the server and speed up pages serving. As you will see, even HTML static pages can still contain some values defined at run time, but these are reduced to the minimum compared to dynamic pages. Most probably you will not want to modify these pages, since the small amount of dynamic content they have allow these pages to inherit from the customizations you have made elsewhere, such as in the CSS, and the page header and footer. 1.2.1 Layout of HTML Static Pages ````````````````````````````````` Static HTML pages are all located in the /opt/invenio/lib/webdoc/ installation directory. These files are organized in 3 directories: - **help:** Help pages available to users of your website - **admin:** Mostly guides for admin users - **hacking:** Mostly guides for administrators and developers These directories do not contain the ``.html`` files, but ``.webdoc`` files. These '*WebDoc*\ ' files are basically HTML with the following main differences: - They only contain the body of your page (only content of the ``<body>`` tag) - You can make use of special tags such as ``<CFG_SITE_URL>`` and ``<CFG_SITE_SUPPORT_EMAIL>``, that will be replaced by http://localhost:4000 and info@invenio-software.org, the values that you should have configured in your ``config.py`` file. - You can internationalize the content. For example: :: <lang> <en>Book</en> <fr>Livre</fr> <de>Buch</de> </lang> will be replaced by "Book" if the user has chosen to display the English version of your site, "Livre" in French or "Buch" in German. Read the `WebDoc syntax guide </help/hacking/webstyle-webdoc-syntax>`__ to learn more about details of the WebDoc syntax. The advantage of not using raw HTML for these static pages is that they can for example reuse the header and footer that you have defined for dynamic pages, and make use of the variables you have defined in your invenio.conf file. In that way you should not need to adapt them to your needs: **they** will adapt themselves to your needs. Any modification should be immediatly visible when looking at the pages from the web: the pages are built "dynamically" and then cached. If you want to force the cache of the pages, use the WebDoc CLI (type ``/opt/invenio/bin/webdoc --help`` to learn more about it). 1.2.2 Layout of Python Dynamic Pages ```````````````````````````````````` The dynamic Python-powered pages can be customized by making use of Invenio templating system that uses a notion of a template skin. How this works? When you edit ``invenio-local.conf`` during installation or later during runtime (when during runtime, you have to run ``inveniocfg --update-config-py`` after editing the conf file), you may choose to use your own templates instead of the provided default ones by editing ``CFG_WEBSTYLE_TEMPLATE_SKIN`` variable. Let us say you put ``ithaca`` there in order to use your own ``ithaca`` style. Now, when you start Apache, then instead of Invenio's usual template files such as ``webbasket_templates.py`` the system will look for file named ``webbasket_templates_ithaca.py`` and will load the template functions from there instead, provided that they exist. (Otherwise it would fall back to the default ones.) How do you create such an ``ithaca`` style templates file? We do not use one of many existing templating frameworks in Python but a very simple programmer-friendly templating system that enables you to use the full power of Python to inherit from the default templates the output generating functions you want to reuse and to write anew only the functions you would like to modify. Let's show an example of how to modify the page footer. Create a file named ``webstyle_templates_ithaca.py`` with the following content: :: from invenio.config import CFG_SITE_LANG from invenio.legacy.webstyle.templates import Template as DefaultTemplate class Template(DefaultTemplate): """Ithaca style templates.""" def tmpl_pagefooter(self, req=None, ln=CFG_SITE_LANG, lastupdated=None, pagefooteradd=""): """ Ithaca style page footer. See the default function for the meaning of parameters. """ out = "" out += """<hr>This site has no footer. </body> </html>""" return out After the file was created, restart Apache and lo, your new ithaca style footer will be seen in action. (A side comment: note that ``tmpl_page_footer()`` is an ideal place to put any local code you may want to execute at the end of web request processing when the main page content was just served to the user. As an example, if you are using Google Analytics, you may want to put just after the above ``out = ""`` statement your GA script code: :: [...] out += """ <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-XXXXX-X']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> """ End of the side comment.) Some further remarks on this templating system: - We have observed that in practice the HTML page designers were ofter Python programmers, therefore we have adopted a programmer-friendly templating system. - You have to know a bit of Python in order to use it. If you don't know Python, do not worry, because you can basically copy and paste the original ``tmpl_foo()`` function definition "as is" into the above-cited example and then you would only modify its HTML snippets. The important thing is to preserve the imports (``from invenio.config import CFG_SITE_LANG``) as in the original ``webstyle_templates.py`` file and to preserve the leading whitespace Pythonic indentation. - You do not have to learn "yet another templating language", you can use the full power of Python. The ``tmpl_foo()`` functions do not contain any business logic in them, their only purpose is to make the HTML presentation of data supplied to them. But, should you need to carry out a little data transformation, you can do it within the ``tmpl_foo()`` function itself, thanks to the full Python power. - If you feel like doing so, you can modify all the ``tmpl_foo()`` functions across all Invenio modules in a way that will completely change the presentation of elements including their content, position and order on the screen. - In practice, it is sufficient to modify the CSS and the webstyle\_templates\_ithaca.py (and possibly websearch\_templates\_ithaca.py) files to achieve most important customizations. - If you would like to discover which method of which template generate which region on the web page, you can switch on the ``CFG_WEBSTYLE_INSPECT_TEMPLATES`` configuration variable in your ``invenio-local.conf`` file and rerun ``sudo -u apache /opt/invenio/bin/inveniocfg --update-config-py``. Then, after optionally running ``bibreformat -a`` and ``webcoll -f`` (if you want to debug search pages) and after having restarted your Apache server (in every case), you will find in your browser that a place-mark has been put next to every region of every page, and that you can hover your mouse pointer over any region of the page in order to discover which module/method/parameters have been used to generate it. This is useful for debugging Python templates and/or for understanding which part of code generates which HTML snippet in the output. - We expect to provide possibly more than one skin with the default distribution, so if you have modified Invenio look and feel in an interesting way, please consider donating us your templates. - When upgrading from one Invenio release to another, you may find out that the default templates have changed in a way that requires changes to your templates (such as an addition of parameters to cover the new functionality). This is inevitable in any templating system; unless you introduce new parameters, you would not see them being printed. Therefore, if you have modified ``tmpl_foo()`` and ``tmpl_bar()``, and you are ugrading to a new release, you may at least briefly check whether the function arguments are the same. A quick check of the body would be helpful too, in case the new release fixed some display-related problems in these functions. In order to help you in this task, we provide a tool to check incompatibilities between your customized templates and the default templates. This tool can be run before doing a ``'make install'``, therefore giving you a chance to fix your templates before upgrading. Just run ``'make check-custom-templates'`` to get the list of problems found with your templates. You can also run this tool any time after the new default templates have been installed, in order to ensure that modifications you have done to your templates are valid. To do so move to your Invenio installation directory, and run::: $ python /opt/invenio/lib/python/invenio/template.py --check-custom-templates 1.3 Look of Bibliographic References ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Bibliographic metadata is formatted using ``formatter`` module. 1.4 Specific Configurations ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Note that the search interface pages may be modified to a large extent in the `WebSearch Admin Interface <websearch-admin>`__ by adding HTML portalboxes on various places on the page (right top, before/after page title, before/after narrow by collection boxes, etc). 2. Detailed Record Pages ------------------------ The web pages displaying the details of a record (such as /record/1) do not only show metadata, but also users' comments and reviews, statistics, etc. This information is organized into tabs. The content of these tabs can be customized on a collection basis. It is also possible to show/hide tabs depending on the displayed collection. The detailed record pages also feature a mini panel at the bottom of the page that links to popular functions (The mini panel is only displayed when *Information* tab is selected). :: +--------------Detailed record page-------------+ | header | |nav. breadcrumb | | | | .--------------------------------------. | | .-|Info.|Ref.|Comm.|Review.|Stats.|Files |-. | | | '--------------------------------------' | | | | | | | | content | | | | | | | '------------------------------------------' | | | | .---------------(Mini Panel)---------------. | | | Mini | Mini | Mini | | | | File | Review | Actions | | | '------------------------------------------' | +-----------------------------------------------+ 2.1 Available tabs ~~~~~~~~~~~~~~~~~~ The following tabs are available: +-------------------------+--------------------------+-----------------------+ | Name | | | | Description | | | | URL (eg. for record | | | | '10') | | | +=========================+==========================+=======================+ | Information | References | Comments | | Show the formatted | Displays the references | Displays the users' | | metadata of the record | (bibliography) of the | comments | | /re | record | http://localhost:4000/re | | | cord/10 | /re \| cord/10/comments | | | | cord/10/references | | +-------------------------+--------------------------+-----------------------+ The mini panel is only displayed when the *Information* tab is selected. It is divided into the following sections: - Files: quick access to full-text file(s) - Review: quick access to reviewing feature - Actions: quick access to several other features 2.2 Showing/Hiding tabs ~~~~~~~~~~~~~~~~~~~~~~~ The `WebSearch admin web interface <websearch-admin>`__ lets you decide for each collection which tabs are to be displayed. Choose a collection to edit in the collection tree and go to its *detailed record page options*. From there you can select which tabs to show for that collection. If you want to apply these settings to the subcollections, select *Also apply to subcollections* before you click on the button. Note that these settings only affect the tabs, not the content of the tabs: even if a tab is not displayed, it is still possible to access its content using its usual url. This is useful if you decide to completely change the detailed record pages, dropping the tab-metaphor (eg. for a side bar) but still want to access the comments, reviews, etc pages. Here are some behaviours you should expect when changing the tabs configuration: - Given that search results pages always link to /record/10, and given the above comment about accessibility of tabs when they are not displayed, the content of the *Information* will always be show when clicking on `detailed record <#>`__ link in search results, even if the *Information* tab is set not to be displayed. - If you select only 1 tab, none of the tabs will be displayed at the top of the page. This also means that whatever tabs you have selected, you users will always see the content of the 'Information' tabs (see above behaviour). - If you select 0 tab, only the content of *Information* tab is shown. None of the tabs, nor the border that usually surrounds the content of the tabs, nor the minipanel are shown. You should choose this option if you decide to drop the tabs metaphor for the detailed record pages. You can then build your own user interface on this almost blank page (See `Customizing content of tabs <#det_page_cust_cont_tabs>`__). -- Note that *Comments* and *Reviews* tabs will not be shown if you have - disabled commenting and reviewing features in your installation, - respectively. (``CFG_WEBCOMMENT_ALLOW_COMMENTS`` and - ``CFG_WEBCOMMENT_ALLOW_REVIEWS`` variable in your config file) 2.3 Customizing content of tabs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The contents of tabs are defined in the following ways: ***Information* tab** The content of this tab is defined by function ``tmpl_detailed_record_metadata(..)`` in ``websearch_templates.py``. By default ``tmpl_detailed_record_metadata`` simply returns the result of the formatting of the metadata by BibFormat using the "HD" output format. It can therefore be collection-specific. ***References* tab** The content of this tab is defined by function ``tmpl_detailed_record_references(..)`` in ``websearch_templates.py``. By default ``tmpl_detailed_record_metadata`` simply returns the result of the formatting of the metadata by BibFormat using the "HDREF" output format. If the result returned by BibFormat is empty, the tab is disabled (visible, but not clickable). It can therefore be collection-specific. -***Comments* and *Reviews* tabs** - The content of these tabs is mainly defined by function - ``tmpl_get_comments(..)`` in ``webcomment_templates.py``. Other - functions in this file are also involved in the display. ***Usage Statistics* tab** The content of this tab is defined by function ``tmpl_detailed_record_statistics(..)`` in ``websearch_templates.py``. If the returned content is empty, then the tabs will be disabled (visible, but cannot be clicked). ***Files* tab** The content of this tab is defined by function ``tmpl_filelist(..)`` in ``websubmit_templates.py``. The content of the mini panel is defined in the following ways: ***Files*** The content of this section is defined by the output format 'HDFILE'. It can therefore be collection-specific. -***Review*** - The content of this section is defined by function - ``tmpl_mini_review(..)`` inside ``webcomment_templates.py`` ***Actions*** The content of this section is defined by the output format ``HDACT``. It can therefore be collection-specific. 2.4 Customizing look of tabs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can customize how tabs look like, as well change the look of the border that surrounds the content of tabs. The mini panel can similarly be customized. Have a look at the following classes in the CDS css stylesheet: - ``detailedrecordtabs`` - ``detailedrecordbox`` - ``detailedrecordminipanel`` - ``top-left, top-right, bottom-left, bottom-right`` - ``detailedrecordminipanel{actions,review,file}, detailedrecordshortreminder`` Note that a tab might be greyed out (disabled) when its content is empty. This is the case for the *References* tab (see `Customizing content of tabs <#det_page_cust_cont_tabs>`__ -> 'References tab') and the *Files* tab (if no file could be found for the record). For more advanced modifications (like changing the HTML code of the tabs), you can modify the ``detailed_record_container(..)`` and ``detailed_record_mini_panel(..)`` functions inside your ``webstyle_templates.py`` file. Custom Redirections ~~~~~~~~~~~~~~~~~~~ It is possible to create custom redirections to URLs within Invenio, by registering a given *unique label* to be used after path **/goto/**. FIXME Command Line Interface ~~~~~~~~~~~~~~~~~~~~~~ :: Usage: gotoadmin [options] Options: -h, --help show this help message and exit Plugin Administration Options: --list-plugins List available GOTO plugins and their documentation --list-broken-plugins List broken GOTO plugins Redirection Manipultation Options: -r LABEL, --register-redirection=LABEL Register a redirection with the provided LABEL -u LABEL, --update-redirection=LABEL Update the redirection specified by the provided LABEL -g LABEL, --get-redirection=LABEL Get all information about a redirection specified by LABEL -d LABEL, --drop-redirection=LABEL Drop an existing redirection specified by LABEL Specific Options: -P PLUGIN, --plugin=PLUGIN Specify the plugin to use when registering or updating a redirection -j PARAMETERS, --json-parameters=PARAMETERS Specify the parameters to provide to the plugin (serialized in JSON) -p PARAM=VALUE, --parameter=PARAM=VALUE Specify a single PARAM=VALUE parameter to be provided to the plugin (alternative to the JSON serialization) diff --git a/invenio/legacy/webcomment/__init__.py b/invenio/legacy/webcomment/__init__.py deleted file mode 100644 index bd8998859..000000000 --- a/invenio/legacy/webcomment/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is part of Invenio. -# Copyright (C) 2014, 2015 CERN. -# -# Invenio is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# Invenio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Invenio; if not, write to the Free Software Foundation, Inc., -# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -"""Legacy WebComment.""" - -import warnings - -from invenio.utils.deprecation import RemovedInInvenio22Warning - -warnings.warn("Legacy WebComment will be removed in 2.2. Please check " - "'invenio.modules.comments' module.", - RemovedInInvenio22Warning) diff --git a/invenio/legacy/webcomment/adminlib.py b/invenio/legacy/webcomment/adminlib.py deleted file mode 100644 index b0bab37df..000000000 --- a/invenio/legacy/webcomment/adminlib.py +++ /dev/null @@ -1,690 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Invenio. -# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2015 CERN. -# -# Invenio is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# Invenio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Invenio; if not, write to the Free Software Foundation, Inc., -# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -__revision__ = "$Id$" - -from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, \ - CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN -from invenio.modules.comments.config import InvenioWebCommentWarning -from invenio.modules.comments.api import query_get_comment, \ - get_reply_order_cache_data -from invenio.utils.url import wash_url_argument -from invenio.legacy.dbquery import run_sql -from invenio.base.i18n import gettext_set_language, wash_language -from invenio.ext.logging import register_exception -from invenio.legacy.webuser import get_user_info, collect_user_info, \ - isUserAdmin -from invenio.modules.access.engine import acc_authorize_action, \ - acc_get_authorized_emails -from invenio.legacy.search_engine import perform_request_search - -import invenio.legacy.template -webcomment_templates = invenio.legacy.template.load('webcomment') - -def getnavtrail(previous = '', ln=CFG_SITE_LANG): - """Get the navtrail""" - previous = wash_url_argument(previous, 'str') - ln = wash_language(ln) - _ = gettext_set_language(ln) - navtrail = """<a class="navtrail" href="%s/help/admin">%s</a> """ % (CFG_SITE_URL, _("Admin Area")) - navtrail = navtrail + previous - return navtrail - -def get_nb_reviews(recID, count_deleted=True): - """ - Return number of reviews for the record recID - - if count_deleted is True, deleted reviews are also counted - """ - query = """SELECT count(*) - FROM "cmtRECORDCOMMENT" c - WHERE c.id_bibrec = %s and c.star_score > 0 - """ - - if not count_deleted: - query += "and c.status != 'dm' and c.status != 'da'" - - res = run_sql(query, (recID,)) - - return res[0][0] - -def get_nb_comments(recID, count_deleted=True): - """ - Return number of comments for the record recID - - if count_deleted is True, deleted comments are also counted - """ - query = """SELECT count(*) - FROM "cmtRECORDCOMMENT" c - WHERE c.id_bibrec = %s and c.star_score = 0 - """ - - if not count_deleted: - query += "and c.status != 'dm' and c.status != 'da'" - - res = run_sql(query, (recID,)) - - return res[0][0] - -def get_user_collections(req): - """ - Return collections for which the user is moderator - """ - user_info = collect_user_info(req) - res = [] - collections = run_sql('SELECT name FROM collection') - for collection in collections: - collection_emails = acc_get_authorized_emails('moderatecomments', collection=collection[0]) - if user_info['email'] in collection_emails or isUserAdmin(user_info): - res.append(collection[0]) - return res - -def perform_request_index(ln=CFG_SITE_LANG): - """ - """ - return webcomment_templates.tmpl_admin_index(ln=ln) - -def perform_request_delete(comID=-1, recID=-1, uid=-1, reviews="", ln=CFG_SITE_LANG): - """ - """ - _ = gettext_set_language(ln) - - from invenio.legacy.search_engine import record_exists - - warnings = [] - - ln = wash_language(ln) - comID = wash_url_argument(comID, 'int') - recID = wash_url_argument(recID, 'int') - uid = wash_url_argument(uid, 'int') - # parameter reviews is deduced from comID when needed - - if comID is not None and recID is not None and uid is not None: - if comID <= 0 and recID <= 0 and uid <= 0: - if comID != -1: - try: - raise InvenioWebCommentWarning(_('Invalid comment ID.')) - except InvenioWebCommentWarning as exc: - register_exception(stream='warning') - warnings.append((exc.message, '')) - #warnings.append(("WRN_WEBCOMMENT_ADMIN_INVALID_COMID",)) - return webcomment_templates.tmpl_admin_delete_form(ln, warnings) - - if comID > 0 and not recID > 0: - comment = query_get_comment(comID) - - if comment: - # Figure out if this is a review or a comment - c_star_score = 5 - if comment[c_star_score] > 0: - reviews = 1 - else: - reviews = 0 - return (perform_request_comments(ln=ln, comID=comID, recID=recID, reviews=reviews), None, warnings) - else: - try: - raise InvenioWebCommentWarning(_('Comment ID %(x_name)s does not exist.', x_name=comID)) - except InvenioWebCommentWarning as exc: - register_exception(stream='warning') - warnings.append((exc.message, '')) - #warnings.append(('WRN_WEBCOMMENT_ADMIN_COMID_INEXISTANT', comID)) - return webcomment_templates.tmpl_admin_delete_form(ln, warnings) - - elif recID > 0: - if record_exists(recID): - comID = '' - reviews = wash_url_argument(reviews, 'int') - return (perform_request_comments(ln=ln, comID=comID, recID=recID, reviews=reviews), None, warnings) - else: - try: - raise InvenioWebCommentWarning(_('Record ID %(x_rec)s does not exist.', x_rec=comID)) - except InvenioWebCommentWarning as exc: - register_exception(stream='warning') - warnings.append((exc.message, '')) - #warnings.append(('WRN_WEBCOMMENT_ADMIN_RECID_INEXISTANT', comID)) - return webcomment_templates.tmpl_admin_delete_form(ln, warnings) - else: - return webcomment_templates.tmpl_admin_delete_form(ln, warnings) - - else: - return webcomment_templates.tmpl_admin_delete_form(ln, warnings) - -def perform_request_users(ln=CFG_SITE_LANG): - """ - """ - ln = wash_language(ln) - - users_data = query_get_users_reported() - return webcomment_templates.tmpl_admin_users(ln=ln, users_data=users_data) - -def query_get_users_reported(): - """ - Get the users who have been reported at least once. - @return: tuple of ct, i.e. (ct, ct, ...) - where ct is a tuple (total_number_reported, total_comments_reported, total_reviews_reported, - total_nb_votes_yes_of_reported, total_nb_votes_total_of_reported, user_id, user_email, user_nickname) - sorted by order of ct having highest total_number_reported - """ - query1 = "SELECT c.nb_abuse_reports, c.nb_votes_yes, c.nb_votes_total, u.id, u.email, u.nickname, c.star_score " \ - """FROM user AS u, "cmtRECORDCOMMENT" AS c """ \ - "WHERE c.id_user=u.id AND c.nb_abuse_reports > 0 " \ - "ORDER BY u.id " - res1 = run_sql(query1) - if type(res1) is None: - return () - users = {} - for cmt in res1: - uid = int(cmt[3]) - if uid in users: - users[uid] = (users[uid][0]+int(cmt[0]), int(cmt[6])>0 and users[uid][1] or users[uid][1]+1, int(cmt[6])>0 and users[uid][2]+1 or users[uid][2], - users[uid][3]+int(cmt[1]), users[uid][4]+int(cmt[2]), int(cmt[3]), cmt[4], cmt[5]) - else: - users[uid] = (int(cmt[0]), int(cmt[6])==0 and 1 or 0, int(cmt[6])>0 and 1 or 0, int(cmt[1]), int(cmt[2]), int(cmt[3]), cmt[4], cmt[5]) - users = users.values() - users.sort() - users.reverse() - users = tuple(users) - return users - -def perform_request_comments(req=None, ln=CFG_SITE_LANG, uid="", comID="", recID="", reviews=0, abuse=False, collection=""): - """ - Display the list of comments/reviews along with information about the comment. - - Display the comment given by its ID, or the list of comments for - the given record ID. - If abuse == True, only list records reported as abuse. - If comID and recID are not provided, list all comments, or all - abused comments (check parameter 'abuse') - """ - ln = wash_language(ln) - uid = wash_url_argument(uid, 'int') - comID = wash_url_argument(comID, 'int') - recID = wash_url_argument(recID, 'int') - reviews = wash_url_argument(reviews, 'int') - collection = wash_url_argument(collection, 'str') - - user_info = collect_user_info(req) - user_collections = ['Show all'] - user_collections.extend(get_user_collections(req)) - if collection and collection != 'Show all': - (auth_code, auth_msg) = acc_authorize_action(req, 'moderatecomments', collection=collection) - if auth_code: - return webcomment_templates.tmpl_admin_comments(ln=ln, uid=uid, - comID=comID, - recID=recID, - comment_data=None, - reviews=reviews, - error=1, - user_collections=user_collections, - collection=collection) - if collection: - if recID or uid: - comments = query_get_comments(uid, comID, recID, reviews, ln, abuse=abuse, user_collections=user_collections, collection=collection) - else: - comments = query_get_comments('', comID, '', reviews, ln, abuse=abuse, user_collections=user_collections, collection=collection) - else: - if recID or uid: - comments = query_get_comments(uid, comID, recID, reviews, ln, abuse=abuse, user_collections=user_collections, collection=user_collections[0]) - else: - comments = query_get_comments('', comID, '', reviews, ln, abuse=abuse, user_collections=user_collections, collection=user_collections[0]) - if comments: - return webcomment_templates.tmpl_admin_comments(ln=ln, uid=uid, - comID=comID, - recID=recID, - comment_data=comments, - reviews=reviews, - error=0, - user_collections=user_collections, - collection=collection) - else: - return webcomment_templates.tmpl_admin_comments(ln=ln, uid=uid, - comID=comID, - recID=recID, - comment_data=comments, - reviews=reviews, - error=2, - user_collections=user_collections, - collection=collection) - - - -def perform_request_hot(req=None, ln=CFG_SITE_LANG, comments=1, top=10, collection="Show all"): - """ - Display the list of hottest comments/reviews along with information about the comment. - - @param req: request object for obtaining user information - @param ln: language - @param comments: boolean activated if using comments, deactivated for reviews - @param top: specify number of results to be shown - @param collection: filter by collection - """ - ln = wash_language(ln) - comments = wash_url_argument(comments, 'int') - top = wash_url_argument(top, 'int') - collection = wash_url_argument(collection, 'str') - - user_info = collect_user_info(req) - user_collections = ['Show all'] - user_collections.extend(get_user_collections(req)) - if collection and collection != 'Show all': - (auth_code, auth_msg) = acc_authorize_action(req, 'moderatecomments', collection=collection) - if auth_code: - return webcomment_templates.tmpl_admin_hot(ln=ln, - comment_data = None, - comments=comments, error=1, user_collections=user_collections, collection=collection) - if collection: - comments_retrieved = query_get_hot(comments, ln, top, user_collections, collection) - else: - comments_retrieved = query_get_hot(comments, ln, top, user_collections, user_collections[0]) - if comments_retrieved: - return webcomment_templates.tmpl_admin_hot(ln=ln, - comment_data=comments_retrieved, - comments=comments, error=0, user_collections=user_collections, collection=collection) - else: - return webcomment_templates.tmpl_admin_hot(ln=ln, - comment_data=comments_retrieved, - comments=comments, error=2, user_collections=user_collections, collection=collection) - - -def perform_request_latest(req=None, ln=CFG_SITE_LANG, comments=1, top=10, collection=""): - """ - Display the list of latest comments/reviews along with information about the comment. - - @param req: request object for obtaining user information - @param ln: language - @param comments: boolean activated if using comments, deactivated for reviews - @param top: Specify number of results to be shown - @param collection: filter by collection - """ - ln = wash_language(ln) - comments = wash_url_argument(comments, 'int') - top = wash_url_argument(top, 'int') - collection = wash_url_argument(collection, 'str') - - user_info = collect_user_info(req) - user_collections = ['Show all'] - user_collections.extend(get_user_collections(req)) - if collection and collection != 'Show all': - (auth_code, auth_msg) = acc_authorize_action(req, 'moderatecomments', collection=collection) - if auth_code: - return webcomment_templates.tmpl_admin_latest(ln=ln, - comment_data=None, - comments=comments, error=1, user_collections=user_collections, collection=collection) - if collection: - comments_retrieved = query_get_latest(comments, ln, top, user_collections, collection) - else: - comments_retrieved = query_get_latest(comments, ln, top, user_collections, user_collections[0]) - if comments_retrieved: - return webcomment_templates.tmpl_admin_latest(ln=ln, - comment_data=comments_retrieved, - comments=comments, error=0, user_collections=user_collections, collection=collection) - else: - return webcomment_templates.tmpl_admin_latest(ln=ln, - comment_data=comments_retrieved, - comments=comments, error=2, user_collections=user_collections, collection=collection) - - -def perform_request_undel_single_com(ln=CFG_SITE_LANG, id=id): - """ - Mark comment referenced by id as active - """ - ln = wash_language(ln) - id = wash_url_argument(id, 'int') - - return query_undel_single_comment(id) - -def query_get_comments(uid, cmtID, recID, reviews, ln, abuse=False, user_collections='', collection=''): - """ - private function - @param user_collections: allowed collections for the user - @param collection: collection to display - @return tuple of comment where comment is - tuple (nickname, uid, date_creation, body, id, status) if ranking disabled or - tuple (nickname, uid, date_creation, body, nb_votes_yes, nb_votes_total, star_score, title, id, status) - """ - qdict = {'id': 0, 'id_bibrec': 1, 'uid': 2, 'date_creation': 3, 'body': 4, - 'status': 5, 'nb_abuse_reports': 6, 'nb_votes_yes': 7, 'nb_votes_total': 8, - 'star_score': 9, 'title': 10, 'email': -2, 'nickname': -1} - query = """SELECT c.id, c.id_bibrec, c.id_user, - DATE_FORMAT(c.date_creation, '%%Y-%%m-%%d %%H:%%i:%%S'), c.body, - c.status, c.nb_abuse_reports, - %s - u.email, u.nickname - FROM "cmtRECORDCOMMENT" c LEFT JOIN user u - ON c.id_user = u.id - %s - ORDER BY c.nb_abuse_reports DESC, c.nb_votes_yes DESC, c.date_creation - """ - select_fields = reviews and 'c.nb_votes_yes, c.nb_votes_total, c.star_score, c.title,' or '' - where_clause = "WHERE " + (reviews and 'c.star_score>0' or 'c.star_score=0') - if uid: - where_clause += ' AND c.id_user=%i' % uid - if recID: - where_clause += ' AND c.id_bibrec=%i' % recID - if cmtID: - where_clause += ' AND c.id=%i' % cmtID - if abuse: - where_clause += ' AND c.nb_abuse_reports>0' - - res = run_sql(query % (select_fields, where_clause)) - collection_records = [] - if collection == 'Show all': - for collection_name in user_collections: - collection_records.extend(perform_request_search(cc=collection_name)) - else: - collection_records.extend(perform_request_search(cc=collection)) - output = [] - for qtuple in res: - if qtuple[qdict['id_bibrec']] in collection_records: - nickname = qtuple[qdict['nickname']] or get_user_info(qtuple[qdict['uid']], ln)[2] - if reviews: - comment_tuple = (nickname, - qtuple[qdict['uid']], - qtuple[qdict['date_creation']], - qtuple[qdict['body']], - qtuple[qdict['nb_votes_yes']], - qtuple[qdict['nb_votes_total']], - qtuple[qdict['star_score']], - qtuple[qdict['title']], - qtuple[qdict['id']], - qtuple[qdict['status']]) - else: - comment_tuple = (nickname, - qtuple[qdict['uid']], - qtuple[qdict['date_creation']], - qtuple[qdict['body']], - qtuple[qdict['id']], - qtuple[qdict['status']]) - general_infos_tuple = (nickname, - qtuple[qdict['uid']], - qtuple[qdict['email']], - qtuple[qdict['id']], - qtuple[qdict['id_bibrec']], - qtuple[qdict['nb_abuse_reports']]) - out_tuple = (comment_tuple, general_infos_tuple) - output.append(out_tuple) - return tuple(output) - -def query_get_hot(comments, ln, top, user_collections, collection): - """ - private function - @param comments: boolean indicating if we want to retrieve comments or reviews - @param ln: language - @param top: number of results to display - @param user_collections: allowed collections for the user - @param collection: collection to display - @return: tuple (id_bibrec, date_last_comment, users, count) - """ - qdict = {'id_bibrec': 0, 'date_last_comment': 1, 'users': 2, 'total_count': 3} - query = """SELECT c.id_bibrec, - DATE_FORMAT(max(c.date_creation), '%%Y-%%m-%%d %%H:%%i:%%S') as date_last_comment, - count(distinct c.id_user) as users, - count(*) as count - FROM "cmtRECORDCOMMENT" c - %s - GROUP BY c.id_bibrec - ORDER BY count(*) DESC - LIMIT %s - """ - where_clause = "WHERE " + (comments and 'c.star_score=0' or 'c.star_score>0') + """ AND c.status='ok' AND c.nb_abuse_reports < %s""" % CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN - - res = run_sql(query % (where_clause, top)) - - collection_records = [] - if collection == 'Show all': - for collection_name in user_collections: - collection_records.extend(perform_request_search(cc=collection_name)) - else: - collection_records.extend(perform_request_search(cc=collection)) - - output = [] - for qtuple in res: - if qtuple[qdict['id_bibrec']] in collection_records: - general_infos_tuple = (qtuple[qdict['id_bibrec']], - qtuple[qdict['date_last_comment']], - qtuple[qdict['users']], - qtuple[qdict['total_count']]) - output.append(general_infos_tuple) - return tuple(output) - -def query_get_latest(comments, ln, top, user_collections, collection): - """ - private function - @param comments: boolean indicating if we want to retrieve comments or reviews - @param ln: language - @param top: number of results to display - @param user_collections: allowed collections for the user - @param collection: collection to display - @return tuple of comment where comment is - tuple (nickname, uid, date_creation, body, id) if latest comments or - tuple (nickname, uid, date_creation, body, star_score, id) if latest reviews - """ - qdict = {'id': 0, 'id_bibrec': 1, 'uid': 2, 'date_creation': 3, 'body': 4, - 'nb_abuse_reports': 5, 'star_score': 6, 'nickname': -1} - query = """SELECT c.id, c.id_bibrec, c.id_user, - DATE_FORMAT(c.date_creation, '%%Y-%%m-%%d %%H:%%i:%%S'), c.body, - c.nb_abuse_reports, - %s - u.nickname - FROM "cmtRECORDCOMMENT" c LEFT JOIN user u - ON c.id_user = u.id - %s - ORDER BY c.date_creation DESC - LIMIT %s - """ - select_fields = not comments and 'c.star_score, ' or '' - where_clause = "WHERE " + (comments and 'c.star_score=0' or 'c.star_score>0') + """ AND c.status='ok' AND c.nb_abuse_reports < %s""" % CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN - - res = run_sql(query % (select_fields, where_clause, top)) - - collection_records = [] - if collection == 'Show all': - for collection_name in user_collections: - collection_records.extend(perform_request_search(cc=collection_name)) - else: - collection_records.extend(perform_request_search(cc=collection)) - output = [] - for qtuple in res: - if qtuple[qdict['id_bibrec']] in collection_records: - nickname = qtuple[qdict['nickname']] or get_user_info(qtuple[qdict['uid']], ln)[2] - if not comments: - comment_tuple = (nickname, - qtuple[qdict['uid']], - qtuple[qdict['date_creation']], - qtuple[qdict['body']], - qtuple[qdict['star_score']], - qtuple[qdict['id']]) - else: - comment_tuple = (nickname, - qtuple[qdict['uid']], - qtuple[qdict['date_creation']], - qtuple[qdict['body']], - qtuple[qdict['id']]) - general_infos_tuple = (nickname, - qtuple[qdict['uid']], - qtuple[qdict['id']], - qtuple[qdict['id_bibrec']], - qtuple[qdict['nb_abuse_reports']]) - - out_tuple = (comment_tuple, general_infos_tuple) - output.append(out_tuple) - return tuple(output) - -def perform_request_del_com(ln=CFG_SITE_LANG, comIDs=[]): - """ - private function - Delete the comments and say whether successful or not - @param ln: language - @param comIDs: list of comment ids - """ - ln = wash_language(ln) - comIDs = wash_url_argument(comIDs, 'list') - # map ( fct, list, arguments of function) - comIDs = map(wash_url_argument, comIDs, ('int '*len(comIDs)).split(' ')[:-1]) - - if not comIDs: - comIDs = map(coerce, comIDs, ('0 '*len(comIDs)).split(' ')[:-1]) - return webcomment_templates.tmpl_admin_del_com(del_res=comIDs, ln=ln) - - del_res = [] - for comID in comIDs: - del_res.append((comID, query_delete_comment_mod(comID))) - return webcomment_templates.tmpl_admin_del_com(del_res=del_res, ln=ln) - -def perform_request_undel_com(ln=CFG_SITE_LANG, comIDs=[]): - """ - private function - Undelete the comments and say whether successful or not - @param ln: language - @param comIDs: list of comment ids - """ - ln = wash_language(ln) - comIDs = wash_url_argument(comIDs, 'list') - # map ( fct, list, arguments of function) - comIDs = map(wash_url_argument, comIDs, ('int '*len(comIDs)).split(' ')[:-1]) - - if not comIDs: - comIDs = map(coerce, comIDs, ('0 '*len(comIDs)).split(' ')[:-1]) - return webcomment_templates.tmpl_admin_undel_com(del_res=comIDs, ln=ln) - - del_res = [] - for comID in comIDs: - del_res.append((comID, query_undel_single_comment(comID))) - return webcomment_templates.tmpl_admin_undel_com(del_res=del_res, ln=ln) - - -def perform_request_del_single_com_mod(ln=CFG_SITE_LANG, id=id): - """ - private function - Delete a single comment requested by a moderator - @param ln: language - @param id: comment id to be deleted - """ - ln = wash_language(ln) - id = wash_url_argument(id, 'int') - return query_delete_comment_mod(id) - -def perform_request_del_single_com_auth(ln=CFG_SITE_LANG, id=id): - """ - private function - Delete a single comment requested by the author - @param ln: language - @param id: comment id to be deleted - """ - ln = wash_language(ln) - id = wash_url_argument(id, 'int') - return query_delete_comment_auth(id) - -def perform_request_unreport_single_com(ln=CFG_SITE_LANG, id=""): - """ - private function - Unreport a single comment - @param ln: language - @param id: comment id to be deleted - """ - ln = wash_language(ln) - id = wash_url_argument(id, 'int') - return query_suppress_abuse_report(id) - -def suppress_abuse_report(ln=CFG_SITE_LANG, comIDs=[]): - """ - private function - suppress the abuse reports for the given comIDs. - @param ln: language - @param comIDs: list of ids to suppress attached reports. - """ - ln = wash_language(ln) - comIDs = wash_url_argument(comIDs, 'list') - # map ( fct, list, arguments of function) - comIDs = map(wash_url_argument, comIDs, ('int '*len(comIDs)).split(' ')[:-1]) - - if not comIDs: - comIDs = map(coerce, comIDs, ('0 '*len(comIDs)).split(' ')[:-1]) - return webcomment_templates.tmpl_admin_del_com(del_res=comIDs, ln=ln) - - del_res = [] - for comID in comIDs: - del_res.append((comID, query_suppress_abuse_report(comID))) - return webcomment_templates.tmpl_admin_suppress_abuse_report(del_res=del_res, ln=ln) - -def query_suppress_abuse_report(comID): - """ suppress abuse report for a given comment - @return: integer 1 if successful, integer 0 if not - """ - query = """UPDATE "cmtRECORDCOMMENT" SET nb_abuse_reports=0, status='ap' WHERE id=%s""" - params = (comID,) - res = run_sql(query, params) - return int(res) - -def query_delete_comment_mod(comID): - """ - delete comment with id comID - @return: integer 1 if successful, integer 0 if not - """ - query1 = """UPDATE "cmtRECORDCOMMENT" SET status='dm' WHERE id=%s""" - params1 = (comID,) - res1 = run_sql(query1, params1) - return int(res1) - -def query_delete_comment_auth(comID): - """ - delete comment with id comID - @return: integer 1 if successful, integer 0 if not - """ - query1 = """UPDATE "cmtRECORDCOMMENT" SET status='da' WHERE id=%s""" - params1 = (comID,) - res1 = run_sql(query1, params1) - return int(res1) - -def query_undel_single_comment(comID): - """ - undelete comment with id comID - @return: integer 1 if successful, integer 0 if not - """ - query = """UPDATE "cmtRECORDCOMMENT" SET status='ok' WHERE id=%s""" - params = (comID,) - res = run_sql(query, params) - return int(res) - -def check_user_is_author(user_id, com_id): - """ Check if the user is the author of the given comment """ - res = run_sql("""SELECT id, id_user FROM "cmtRECORDCOMMENT" WHERE id=%s and id_user=%s""", (str(com_id), str(user_id))) - if res: - return 1 - return 0 - -def migrate_comments_populate_threads_index(): - """ - Fill in the `reply_order_cached_data' columns in cmtRECORDCOMMENT and - bskRECORDCOMMENT tables with adequate values so that thread - are displayed correctly. - """ - # Update WebComment comments - res = run_sql("""SELECT id FROM "cmtRECORDCOMMENT" WHERE reply_order_cached_data is NULL""") - for row in res: - reply_order_cached_data = get_reply_order_cache_data(row[0]) - run_sql("""UPDATE "cmtRECORDCOMMENT" set reply_order_cached_data=%s WHERE id=%s""", - (reply_order_cached_data, row[0])) - - # Update WebBasket comments - res = run_sql("""SELECT id FROM "bskRECORDCOMMENT" WHERE reply_order_cached_data is NULL""") - for row in res: - reply_order_cached_data = get_reply_order_cache_data(row[0]) - run_sql("""UPDATE "cmtRECORDCOMMENT" set reply_order_cached_data=%s WHERE id=%s""", - (reply_order_cached_data, row[0])) diff --git a/invenio/legacy/webcomment/doc/admin/webcomment-admin-guide.webdoc b/invenio/legacy/webcomment/doc/admin/webcomment-admin-guide.webdoc deleted file mode 100644 index cb08c3bfa..000000000 --- a/invenio/legacy/webcomment/doc/admin/webcomment-admin-guide.webdoc +++ /dev/null @@ -1,448 +0,0 @@ -# -*- mode: html; coding: utf-8; -*- - -# This file is part of Invenio. -# Copyright (C) 2007, 2008, 2010, 2011 CERN. -# -# Invenio is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# Invenio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Invenio; if not, write to the Free Software Foundation, Inc., -# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -<!-- WebDoc-Page-Title: WebComment Admin Guide --> -<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> --> -<!-- WebDoc-Page-Revision: $Id$ --> - -<style type="text/css"> -<!-- -.guidecommentbox { - float:left; - clear:both; - font-size:small; - margin-bottom:12px -} -.guidecommentboxcontent { - border:1px solid #000; - padding:1px; -} -.guidecommentboxaction { - text-decoration:underline; - text-align:right; -} - ---> -</style> - -<h2>Contents</h2> - -<strong>1. <a href="#1">Overview</a></strong><br /> -<strong>2. <a href="#2">Managing WebComment</a></strong><br /> - 2.1<a href="#2.1"> Viewing comments/review information</a><br /> -<strong>3. <a href="#3">Configuring WebComment</a></strong><br /> - 3.1<a href="#3.1"> Configuring moderator per collection</a><br /> - 3.2<a href="#3.2"> Enabling LaTeX/MathJax in comments</a><br /> - 3.3<a href="#3.3"> Configuring threading</a><br /> - 3.4<a href="#3.4"> Configuring commenting rounds</a><br /> - 3.5<a href="#3.5"> Configuring restrictions</a><br /> - 3.5.1<a href="#3.5.1"> Record-level restrictions</a><br /> - 3.5.2<a href="#3.5.2"> Discussion-level restrictions</a><br /> - 3.5.3<a href="#3.5.3"> Comment-level restrictions</a><br /> - 3.6<a href="#3.6"> Configuring file attachments</a><br /> - 3.7<a href="#3.7"> Configuring email notifications</a><br /> - -<a name="1"></a><h2>1. Overview</h2> -<p> -WebComment manages all aspects related to comments. From the admin interface it is -possible to check comment statistics as well as manage comments reported. -If the user is authorized for moderation, when viewing a record special links are -displayed to execute actions such as delete/undelete or unreport comments. -</p> - -<a name="2"></a><h2>2. Managing WebComment</h2> - -<a name="2.1"></a><h3>2.1 Viewing comments/review information</h3> - -<p> From the admin interface it is possible to view statistics related -to the most commented/reviewed records and latest comments/reviews posted. -</p> - -<p> Depending on the role the user has, it will be possible to view information -related to the collections the user is authorized to. -</p> - -<a name="3"></a><h2>3. Configuring WebComment</h2> - -<a name="3.1"></a><h3>3.1 Configuring moderator per collection</h3> - -<p>Configuration to specify which user is moderator of a given collection -is done through WebAccess administration interface.</p> - -<p>In order to permit a given user to be a comment moderator, the following steps have to be followed: -</p> -<ul> - <li> Create a role for the user who is going to moderate (one for each moderator)</li> - <li> Go to the Action Administration Menu inside of WebAccess admin interface <br /> - and assign this role to the action 'moderatecomments'. Specify as argument <br /> - the collections allowed for the user.</li> - <li> In case you want to give the superadmin user access to all collections, follow <br /> - the steps above and write * as the collection argument. </li> -</ul> - -<a name="3.2"></a><h3>3.2 Enabling LaTeX/MathJax in comments</h3> - -<p> -It is possible to enable LaTeX rendering in comments with the MathJax module. In order to do that, - it is necessary to set the following directive in the invenio configuration file: -<pre>CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS = 1</pre> -</p> - -<a name="3.3"></a><h3>3.3 Configuring threading</h3> - -<p> -Replies to comments can be organized in threads. It is possible to -configure the maximum depth of the threads with -the <code>CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH</code> variable in -the <code>invenio-local.conf</code> file. <br/> -Set <code>CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = -1</code> to not limit the depth.<br/> -Set <code>CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = 0</code> for a "flat" discussion.<br/> -Set <code>CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = 1</code> for one-level deep threads (<b>default</b>)<br/> -etc.. -</p> - -<p> -The main advantage of setting a maximum depth is to not enter into -deep, meaningless indentations when users might not understand the -concept of threads, and mix the "reply to" and "add comment" -actions. When <code>CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH</code> is reached, -the level of discussion becomes a "flat" discussion. -</p> -<p> - -For example, with <code>CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH = 1</code>, replies -to comments would go through to the following states: - -<table style="text-align:center"> -<tr><td>State 1</td><td></td><td>State 2</td><td></td><td>State 3</td><td></td><td>State 4</td></tr> -<tr><td valign="top"> -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 1 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 2 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> -</td> -<td style="padding:40px" valign="top"> -<pre> -Reply to -Comment 1 ---------> -</pre> -</td> -<td valign="top"> -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 1 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox" style="margin-left:20px"> -<div class="guidecommentboxcontent"> -<b>Comment 3</b> -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 2 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -</td> -<td style="padding:40px" valign="top"> -<pre style="margin-top:30px"> -Reply to -Comment 3 ---------> -</pre> -</td> -<td valign="top"> -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 1 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox" style="margin-left:20px"> -<div class="guidecommentboxcontent"> -Comment 3 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox" style="margin-left:20px"> -<div class="guidecommentboxcontent"> -<b>Comment 4</b> -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 2 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> -</td> -<td style="padding:40px" valign="top"> -<pre style="margin-top:60px"> -Reply to -Comment 3 ---------> -</pre> -</td> -<td valign="top"> -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 1 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox" style="margin-left:20px"> -<div class="guidecommentboxcontent"> -Comment 3 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox" style="margin-left:20px"> -<div class="guidecommentboxcontent"> -Comment 4 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox" style="margin-left:20px"> -<div class="guidecommentboxcontent"> -<b>Comment 5</b> -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Comment 2 -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> -</td></tr> -</table> - -</p> - -<a name="3.4"></a><h3>3.4 Configuring commenting rounds</h3> - -<p>You can configure WebComment to group comments according to -the status of the commented record. This enables the creation of -commenting <em>rounds</em>, so that comments belonging -to the same round are grouped together, and comments belonging -to past rounds are less prominently displayed. -</p> - -<p> -For example, let's say that a collaboration is commenting a draft -document: once a new revision of the document is uploaded by the -author, the already existing comments are grouped together and moved -to archive. The discussion page will then appear empty, and a new -commenting round can start.<br/> - -The past commenting rounds are still visible as small individual -links at the top of the discussion. A click on one of these links -reveals the comments belonging to this group/round. It is then also possible -to continue replying to comments submitted during this past round, -though it is not possible to add a new comment (not a reply). -</p> - -<table> -<tr> -<td valign="top"> -<span style="text-decoration:underline;font-size:small"> >Comments for revision 1</span><br/> -<span style="text-decoration:underline;font-size:small"> >Comments for revision 2</span><br/> -<span style="text-decoration:underline;font-size:small"> v Comments for revision 3</span><br/><br/> - -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Great paper. Check typo line 12. -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Are you sure that figure 5 is ok? -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> - -<div class="guidecommentbox"> -<div class="guidecommentboxcontent"> -Please include the standard logo. -</div> -<div class="guidecommentboxaction">Reply to</div> -</div> -</td> -<td valign="top" style="padding:40px" > -<pre style="margin-top:10px"> -New revision - of file -------------> -</pre> -</td> -<td valign="top"> -<span style="text-decoration:underline;font-size:small"> >Comments for revision 1</span><br/> -<span style="text-decoration:underline;font-size:small"> >Comments for revision 2</span><br/> -<span style="text-decoration:underline;font-size:small"> > Comments for revision 3</span><br/> -<span style="text-decoration:underline;font-size:small"> v Comments for revision 4</span><br/><br/> -<p style="text-align:center"><em>(blank)</em></p -</td> -</tr> -</table> - - -<p> -In order to know what is the current round, the MARC metadata of the -commented record must specify the name of the current group to which new -comments have to be attached. The MARC field specifying the round "name" -can be configured with -the <code>CFG_WEBCOMMENT_ROUND_DATAFIELD</code> of -the <code>invenio-local.conf</code> file. There you can set the MARC code -that contains the round name, for each collection you want to enable rounds. -</p> - -<p>Note that whatever the current round is, a reply to a -comment will always belong to the round of the parent.</p> - -<p> -<em>How to modify the MARC field with the round name?</em>: -this is up to the admin of the system, but it would typically be done -by a WebSubmit submission. This field might already exists if you -store a revision number in your metadata, or date, etc. -</p> - -<p> -<em>How to restrict commenting rounds?</em>: you can combine rounds -with the comment-level restrictions documented in -section <a href="#3.5.3">Comment-level restrictions</a>, which uses a -similar mechanism to protect comments. -</p> - -<a name="3.5"></a><h3>3.5 Configuring restrictions</h3> - -Comments restrictions can be configured at several levels, detailed in -the next sections. Note that restrictions applied to comments also -apply to the files attached to comments. - -<a name="3.5.1"></a><h4>3.5.1 Record-level restrictions</h4> - -<p> -When a record is restricted via its collection -(<code>viewrestrcoll</code> WebAccess action), the comments submitted -for this record are restricted to users who can view the record. -</p> - -<a name="3.5.2"></a><h4>3.5.2 Discussion-level restrictions</h4> -<p> -Discussion pages can be restricted with the WebAccess <code>viewcomment</code> -and <code>sendcomment</code> actions. This let you define who can read or send -comments in particular collections. -</p> - -<a name="3.5.3"></a><h4>3.5.3 Comment-level restrictions</h4> -<p> -Restrictions can be applied at the level of individual records. When -submitting a comment, WebComment checks in the record metadata if some -restriction should be applied. The field where WebComment is looking -for restrictions can be configured in the -the <code>CFG_WEBCOMMENT_RESTRICTION_DATAFIELD</code> of -the <code>invenio-local.conf</code> file. There you can define for each -collection the MARC code containing the restriction information. -</p> -<p> -For the restriction to be applied, an authorisation for -the <code>viewrestrcomment</code> WebAccess action must be set up. -The "status" parameter of the <code>viewrestrcomment</code> must match -the value in the MARC metadata. This lets you define different -restrictions based on the value in the metadata. -<br/>Unless the status is empty, the comment will be restricted, even if -no role is specifically linked to the <code>viewrestrcomment</code> -action for this status.</p> - -<p>Note that whatever the status of the record is, a reply to a -comment always inherits the restriction of the parent.</p> - -<em>How to modify the MARC field with the restriction?</em>: -this is up to the admin of the system, but it would typically be done by -a WebSubmit submission. -</p> - - -<a name="3.6"></a><h3>3.6 Configuring file attachments</h3> - -<p> -WebComment allows authorized users to attach files to submitted -comments. This is only available to logged in users, who have been -authorized via the <code>attachcommentfile</code> WebAccess action. By -default no user (except admin) can attach files.</p> - -<p> -In addition, you can configure the maximum number of allowed attachments with the -<code>CFG_WEBCOMMENT_MAX_ATTACHED_FILES</code> (default 5) variable of -the the <code>invenio-local.conf</code> -file. Set <code>CFG_WEBCOMMENT_MAX_ATTACHED_FILES = 0</code> for -unlimited number of files.<br/> -Note that this is only applicable only if you installed the jQuery plugins -with <code>make install-jquery-plugins</code>. -</p> - -<p> -You can set up the maximum size (in bytes) of attachments with -the <code>CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE</code> variable of the -the <code>invenio-local.conf</code> file. -</p> - -<a name="3.7"></a><h3>3.7 Configuring email notifications</h3> - -<p> -WebComment allows users to subscribe to discussions directly from the -web interface. In addition you can set up automatic notifications that -don't require the users to subscribe. The main use case for this is to -notify the author that a new comment has been submitted on her -document. -</p> - -<p> -WebComment checks for automatic notifications of comments in the MARC -record. It specifically looks for emails in fields defined by -the <code>CFG_WEBCOMMENT_EMAIL_REPLIES_TO</code> variable of the -the <code>invenio-local.conf</code> file. There you can define -the fields to look for for specific collections. -</p> diff --git a/invenio/legacy/webcomment/templates.py b/invenio/legacy/webcomment/templates.py deleted file mode 100644 index 1816c9eab..000000000 --- a/invenio/legacy/webcomment/templates.py +++ /dev/null @@ -1,2613 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Invenio. -# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 CERN. -# -# Invenio is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# Invenio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Invenio; if not, write to the Free Software Foundation, Inc., -# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -"""HTML Templates for commenting features """ - -__revision__ = "$Id$" - -import cgi - -# Invenio imports -from invenio.utils.url import create_html_link, create_url -from invenio.legacy.webuser import get_user_info, collect_user_info, isGuestUser, get_email -from invenio.utils.date import convert_datetext_to_dategui -from invenio.utils.mail import email_quoted_txt2html -from invenio.config import CFG_SITE_URL, \ - CFG_SITE_SECURE_URL, \ - CFG_BASE_URL, \ - CFG_SITE_LANG, \ - CFG_SITE_NAME, \ - CFG_SITE_NAME_INTL,\ - CFG_SITE_SUPPORT_EMAIL,\ - CFG_WEBCOMMENT_ALLOW_REVIEWS, \ - CFG_WEBCOMMENT_ALLOW_COMMENTS, \ - CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR, \ - CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN, \ - CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION, \ - CFG_CERN_SITE, \ - CFG_SITE_RECORD, \ - CFG_WEBCOMMENT_MAX_ATTACHED_FILES, \ - CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE -from invenio.utils.html import get_html_text_editor, create_html_select -from invenio.base.i18n import gettext_set_language -from invenio.modules.formatter import format_record -from invenio.modules.access.engine import acc_authorize_action -from invenio.modules.access.control import acc_get_user_roles_from_user_info, acc_get_role_id -from invenio.legacy.bibrecord import get_fieldvalues - -class Template: - """templating class, refer to webcomment.py for examples of call""" - - def tmpl_get_first_comments_without_ranking(self, recID, ln, comments, nb_comments_total, warnings): - """ - @param recID: record id - @param ln: language - @param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks - @param nb_comments_total: total number of comments for this record - @param warnings: list of warning tuples (warning_text, warning_color) - @return: html of comments - """ - - # load the right message language - _ = gettext_set_language(ln) - - # naming data fields of comments - c_nickname = 0 - c_user_id = 1 - c_date_creation = 2 - c_body = 3 - c_status = 4 - c_nb_reports = 5 - c_id = 6 - - warnings = self.tmpl_warnings(warnings, ln) - - # write button - write_button_label = _("Write a comment") - write_button_link = '%s/%s/%s/comments/add' % (CFG_SITE_URL, CFG_SITE_RECORD, recID) - write_button_form = '<input type="hidden" name="ln" value="%s"/>' % ln - write_button_form = self.createhiddenform(action=write_button_link, method="get", text=write_button_form, button=write_button_label) - - # comments - comment_rows = '' - last_comment_round_name = None - comment_round_names = [comment[0] for comment in comments] - if comment_round_names: - last_comment_round_name = comment_round_names[-1] - - for comment_round_name, comments_list in comments: - comment_rows += '<div id="cmtRound%s" class="cmtRound">' % (comment_round_name) - if comment_round_name: - comment_rows += '<div class="webcomment_comment_round_header">' + \ - _('%(x_nb)i Comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name} + "</div>" - else: - comment_rows += '<div class="webcomment_comment_round_header">' + \ - _('%(x_nb)i Comments') % {'x_nb': len(comments_list),} + "</div>" - for comment in comments_list: - if comment[c_nickname]: - nickname = comment[c_nickname] - display = nickname - else: - (uid, nickname, display) = get_user_info(comment[c_user_id]) - messaging_link = self.create_messaging_link(nickname, display, ln) - comment_rows += """ - <tr> - <td>""" - report_link = '%s/%s/%s/comments/report?ln=%s&comid=%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln, comment[c_id]) - reply_link = '%s/%s/%s/comments/add?ln=%s&comid=%s&action=REPLY' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln, comment[c_id]) - comment_rows += self.tmpl_get_comment_without_ranking( - req=None, - ln=ln, - nickname=messaging_link, - comment_uid=comment[c_user_id], - date_creation=comment[c_date_creation], - body=comment[c_body], - status=comment[c_status], - nb_reports=comment[c_nb_reports], - reply_link=reply_link, - report_link=report_link, - recID=recID, - com_id=comment[c_id] - ) - comment_rows += """ - <br /> - <br /> - </td> - </tr>""" - # Close comment round - comment_rows += '</div>' - - # output - if nb_comments_total > 0: - out = warnings - comments_label = len(comments) > 1 and _("Showing the latest %(x_num)i comments:", x_num=len(comments)) \ - or "" - out += """ -<div class="video_content_clear"></div> -<table class="webcomment_header_comments"> - <tr> - <td class="blocknote">%(comment_title)s</td> - </tr> -</table> -<div class="websomment_header_comments_label">%(comments_label)s</div> - %(comment_rows)s -%(view_all_comments_link)s -%(write_button_form)s<br />""" % \ - {'comment_title': _("Discuss this document"), - 'comments_label': comments_label, - 'nb_comments_total' : nb_comments_total, - 'recID': recID, - 'comment_rows': comment_rows, - 'tab': ' '*4, - 'siteurl': CFG_SITE_URL, - 's': nb_comments_total>1 and 's' or "", - 'view_all_comments_link': nb_comments_total>0 and '''<a class="webcomment_view_all_comments" href="%s/%s/%s/comments/display">View all %s comments</a>''' \ - % (CFG_SITE_URL, CFG_SITE_RECORD, recID, nb_comments_total) or "", - 'write_button_form': write_button_form, - 'nb_comments': len(comments) - } - if not comments: - out = """ -<!-- comments title table --> -<table class="webcomment_header_comments"> - <tr> - <td class="blocknote">%(discuss_label)s:</td> - </tr> -</table> -<div class="webcomment_header_details">%(detailed_info)s -<br /> -</div> -%(form)s -""" % {'form': write_button_form, - 'discuss_label': _("Discuss this document"), - 'detailed_info': _("Start a discussion about any aspect of this document.") - } - - return out - - def tmpl_record_not_found(self, status='missing', recID="", ln=CFG_SITE_LANG): - """ - Displays a page when bad or missing record ID was given. - @param status: 'missing' : no recID was given - 'inexistant': recID doesn't have an entry in the database - 'deleted': : recID has been deleted - 'nan' : recID is not a number - 'invalid' : recID is an error code, i.e. in the interval [-99,-1] - @param return: body of the page - """ - _ = gettext_set_language(ln) - if status == 'inexistant': - body = _("Sorry, the record %(x_rec)s does not seem to exist.", x_rec=(recID,)) - elif status in ('deleted'): - body = _("The record has been deleted.") - elif status in ('nan', 'invalid'): - body = _("Sorry, %(x_rec)s is not a valid ID value.", x_rec=(recID,)) - else: - body = _("Sorry, no record ID was provided.") - - body += "<br /><br />" - link = "<a href=\"%s?ln=%s\">%s</a>." % (CFG_SITE_URL, ln, CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)) - body += _("You may want to start browsing from %(x_link)s", x_link=link) - return body - - def tmpl_get_first_comments_with_ranking(self, recID, ln, comments=None, nb_comments_total=None, avg_score=None, warnings=[]): - """ - @param recID: record id - @param ln: language - @param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks - @param nb_comments_total: total number of comments for this record - @param avg_score: average score of all reviews - @param warnings: list of warning tuples (warning_text, warning_color) - @return: html of comments - """ - # load the right message language - _ = gettext_set_language(ln) - - # naming data fields of comments - c_nickname = 0 - c_user_id = 1 - c_date_creation = 2 - c_body = 3 - c_status = 4 - c_nb_reports = 5 - c_nb_votes_yes = 6 - c_nb_votes_total = 7 - c_star_score = 8 - c_title = 9 - c_id = 10 - - warnings = self.tmpl_warnings(warnings, ln) - - #stars - if avg_score > 0: - avg_score_img = 'stars-' + str(avg_score).split('.')[0] + '-' + str(avg_score).split('.')[1] + '.png' - else: - avg_score_img = "stars-0-0.png" - - # voting links - useful_dict = { 'siteurl' : CFG_SITE_URL, - 'CFG_SITE_RECORD' : CFG_SITE_RECORD, - 'recID' : recID, - 'ln' : ln, - 'yes_img' : 'smchk_gr.gif', #'yes.gif', - 'no_img' : 'iconcross.gif' #'no.gif' - } - link = '<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/vote?ln=%(ln)s&comid=%%(comid)s' % useful_dict - useful_yes = link + '&com_value=1">' + _("Yes") + '</a>' - useful_no = link + '&com_value=-1">' + _("No") + '</a>' - - #comment row - comment_rows = ' ' - last_comment_round_name = None - comment_round_names = [comment[0] for comment in comments] - if comment_round_names: - last_comment_round_name = comment_round_names[-1] - - for comment_round_name, comments_list in comments: - comment_rows += '<div id="cmtRound%s" class="cmtRound">' % (comment_round_name) - comment_rows += _('%(x_nb)i comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name} + "<br/>" - for comment in comments_list: - if comment[c_nickname]: - nickname = comment[c_nickname] - display = nickname - else: - (uid, nickname, display) = get_user_info(comment[c_user_id]) - messaging_link = self.create_messaging_link(nickname, display, ln) - - comment_rows += ''' - <tr> - <td>''' - report_link = '%s/%s/%s/reviews/report?ln=%s&comid=%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln, comment[c_id]) - comment_rows += self.tmpl_get_comment_with_ranking( - req=None, - ln=ln, - nickname=messaging_link, - comment_uid=comment[c_user_id], - date_creation=comment[c_date_creation], - body=comment[c_body], - status=comment[c_status], - nb_reports=comment[c_nb_reports], - nb_votes_total=comment[c_nb_votes_total], - nb_votes_yes=comment[c_nb_votes_yes], - star_score=comment[c_star_score], - title=comment[c_title], - report_link=report_link, - recID=recID - ) - comment_rows += ''' - %s %s / %s<br />''' % (_("Was this review helpful?"), useful_yes % {'comid':comment[c_id]}, useful_no % {'comid':comment[c_id]}) - comment_rows += ''' - <br /> - </td> - </tr>''' - # Close comment round - comment_rows += '</div>' - - # write button - write_button_link = '''%s/%s/%s/reviews/add''' % (CFG_SITE_URL, CFG_SITE_RECORD, recID) - write_button_form = ' <input type="hidden" name="ln" value="%s"/>' % ln - write_button_form = self.createhiddenform(action=write_button_link, method="get", text=write_button_form, button=_("Write a review")) - - if nb_comments_total > 0: - avg_score_img = str(avg_score_img) - avg_score = str(avg_score) - nb_comments_total = str(nb_comments_total) - score = '<b>' - score += _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \ - {'x_nb_score': '</b><img src="' + CFG_SITE_URL + '/img/' + avg_score_img + '" alt="' + avg_score + '" />', - 'x_nb_reviews': nb_comments_total} - useful_label = _("Readers found the following %(x_name)s reviews to be most helpful.", x_name=len(comments) if len(comments) > 0 else '') - view_all_comments_link ='<a class"webcomment_view_all_reviews" href="%s/%s/%s/reviews/display?ln=%s&do=hh">' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln) - view_all_comments_link += _("View all %(x_name)s reviews", x_name=nb_comments_total) - view_all_comments_link += '</a><br />' - - out = warnings + """ - <!-- review title table --> - <table class="webcomment_header_ratings"> - <tr> - <td class="blocknote">%(comment_title)s:</td> - </tr> - </table> - %(score_label)s<br /> - %(useful_label)s - <!-- review table --> - <table class="webcomment_review_title_table"> - %(comment_rows)s - </table> - %(view_all_comments_link)s - %(write_button_form)s<br /> - """ % \ - { 'comment_title' : _("Rate this document"), - 'score_label' : score, - 'useful_label' : useful_label, - 'recID' : recID, - 'view_all_comments' : _("View all %(x_name)s reviews", x_name=(nb_comments_total,)), - 'write_comment' : _("Write a review"), - 'comment_rows' : comment_rows, - 'tab' : ' '*4, - 'siteurl' : CFG_SITE_URL, - 'view_all_comments_link': nb_comments_total>0 and view_all_comments_link or "", - 'write_button_form' : write_button_form - } - else: - out = ''' - <!-- review title table --> - <table class="webcomment_header_ratings"> - <tr> - <td class="blocknote"><div class="webcomment_review_first_introduction">%s:</td> - </tr> - </table> - %s<br /> - %s - <br />''' % (_("Rate this document"), - _('Be the first to review this document.</div>'), - write_button_form) - return out - - def tmpl_get_comment_without_ranking(self, req, ln, nickname, comment_uid, date_creation, body, status, nb_reports, reply_link=None, report_link=None, undelete_link=None, delete_links=None, unreport_link=None, recID=-1, com_id='', attached_files=None, collapsed_p=False, admin_p=False): - """ - private function - @param req: request object to fetch user info - @param ln: language - @param nickname: nickname - @param date_creation: date comment was written - @param body: comment body - @param status: status of the comment: - da: deleted by author - dm: deleted by moderator - ok: active - @param nb_reports: number of reports the comment has - @param reply_link: if want reply and report, give the http links - @param report_link: if want reply and report, give the http links - @param undelete_link: http link to delete the message - @param delete_links: http links to delete the message - @param unreport_link: http link to unreport the comment - @param recID: recID where the comment is posted - @param com_id: ID of the comment displayed - @param attached_files: list of attached files - @param collapsed_p: if the comment should be collapsed or not - @return: html table of comment - """ - from invenio.legacy.search_engine import guess_primary_collection_of_a_record - # load the right message language - _ = gettext_set_language(ln) - user_info = collect_user_info(req) - date_creation = convert_datetext_to_dategui(date_creation, ln=ln) - if attached_files is None: - attached_files = [] - out = '' - final_body = email_quoted_txt2html(body) - title = nickname - title += '<a name="C%s" id="C%s"></a>' % (com_id, com_id) - links = '' - if not isGuestUser(user_info['uid']): - # Add link to toggle comment visibility - links += create_html_link(CFG_SITE_URL + '/' + CFG_SITE_RECORD + '/' + str(recID) + '/comments/toggle', - {'comid': com_id, 'ln': ln, 'collapse': collapsed_p and '0' or '1', 'referer': user_info['uri']}, - _("Close"), - {'onclick': "return toggle_visibility(this, %s, 'fast');" % com_id}, - escape_linkattrd=False) - moderator_links = '' - if reply_link: - links += '<a class="webcomment_comment_reply" href="' + reply_link +'">' + _("Reply") +'</a>' - if report_link and status != 'ap': - links += '<a class="webcomment_comment_report" href="' + report_link +'">' + _("Report abuse") + '</a>' - # Check if user is a comment moderator - record_primary_collection = guess_primary_collection_of_a_record(recID) - (auth_code, auth_msg) = acc_authorize_action(user_info, 'moderatecomments', collection=record_primary_collection) - if status in ['dm', 'da'] and not admin_p: - if not auth_code: - if status == 'dm': - final_body = '<div class="webcomment_deleted_comment_message">(Comment deleted by the moderator) - not visible for users<br /><br />' +\ - final_body + '</div>' - else: - final_body = '<div class="webcomment_deleted_comment_message">(Comment deleted by the author) - not visible for users<br /><br />' +\ - final_body + '</div>' - - links = '' - moderator_links += '<a class="webcomment_deleted_comment_undelete" href="' + undelete_link + '">' + _("Undelete comment") + '</a>' - else: - if status == 'dm': - final_body = '<div class="webcomment_deleted_comment_message">Comment deleted by the moderator</div>' - else: - final_body = '<div class="webcomment_deleted_comment_message">Comment deleted by the author</div>' - links = '' - else: - if not auth_code: - moderator_links += '<a class="webcomment_comment_delete" href="' + delete_links['mod'] +'">' + _("Delete comment") + '</a>' - elif (user_info['uid'] == comment_uid) and CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION: - moderator_links += '<a class="webcomment_comment_delete" href="' + delete_links['auth'] +'">' + _("Delete comment") + '</a>' - - if nb_reports >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN: - if not auth_code: - final_body = '<div class="webcomment_reported_comment_message">(Comment reported. Pending approval) - not visible for users<br /><br />' + final_body + '</div>' - links = '' - moderator_links += '<a class="webcomment_reported_comment_unreport" href="' + unreport_link +'">' + _("Unreport comment") + '</a>' - else: - final_body = '<div class="webcomment_comment_pending_approval_message">This comment is pending approval due to user reports</div>' - links = '' - if links and moderator_links: - links = links + moderator_links - elif not links: - links = moderator_links - - attached_files_html = '' - if attached_files: - attached_files_html = '<div class="cmtfilesblock"><b>%s:</b><br/>' % (len(attached_files) == 1 and _("Attached file") or _("Attached files")) - for (filename, filepath, fileurl) in attached_files: - attached_files_html += create_html_link(urlbase=fileurl, urlargd={}, - link_label=cgi.escape(filename)) + '<br />' - attached_files_html += '</div>' - - toggle_visibility_block = '' - if not isGuestUser(user_info['uid']): - toggle_visibility_block = """<div class="webcomment_toggle_visibility"><a id="collapsible_ctr_%(comid)s" class="%(collapse_ctr_class)s" href="%(toggle_url)s" onclick="return toggle_visibility(this, %(comid)s);" title="%(collapse_label)s"><span style="display:none">%(collapse_label)s</span></a></div>""" % \ - {'comid': com_id, - 'toggle_url': create_url(CFG_SITE_URL + '/' + CFG_SITE_RECORD + '/' + str(recID) + '/comments/toggle', {'comid': com_id, 'ln': ln, 'collapse': collapsed_p and '0' or '1', 'referer': user_info['uri']}), - 'collapse_ctr_class': collapsed_p and 'webcomment_collapse_ctr_right' or 'webcomment_collapse_ctr_down', - 'collapse_label': collapsed_p and _("Open") or _("Close")} - - out += """ -<div class="webcomment_comment_box"> - %(toggle_visibility_block)s - <div class="webcomment_comment_avatar"><img class="webcomment_comment_avatar_default" src="%(site_url)s/img/user-icon-1-24x24.gif" alt="avatar" /></div> - <div class="webcomment_comment_content"> - <div class="webcomment_comment_title"> - %(title)s - <div class="webcomment_comment_date">%(date)s</div> - <a class="webcomment_permalink" title="Permalink to this comment" href="#C%(comid)s">¶</a> - </div> - <div class="collapsible_content" id="collapsible_content_%(comid)s" style="%(collapsible_content_style)s"> - <blockquote> - %(body)s - </blockquote> - %(attached_files_html)s - - <div class="webcomment_comment_options">%(links)s</div> - </div> - <div class="clearer"></div> - </div> - <div class="clearer"></div> -</div>""" % \ - {'title' : title, - 'body' : final_body, - 'links' : links, - 'attached_files_html': attached_files_html, - 'date': date_creation, - 'site_url': CFG_SITE_URL, - 'comid': com_id, - 'collapsible_content_style': collapsed_p and 'display:none' or '', - 'toggle_visibility_block': toggle_visibility_block, - } - return out - - def tmpl_get_comment_with_ranking(self, req, ln, nickname, comment_uid, date_creation, body, status, nb_reports, nb_votes_total, nb_votes_yes, star_score, title, report_link=None, delete_links=None, undelete_link=None, unreport_link=None, recID=-1, admin_p=False): - """ - private function - @param req: request object to fetch user info - @param ln: language - @param nickname: nickname - @param date_creation: date comment was written - @param body: comment body - @param status: status of the comment - @param nb_reports: number of reports the comment has - @param nb_votes_total: total number of votes for this review - @param nb_votes_yes: number of positive votes for this record - @param star_score: star score for this record - @param title: title of review - @param report_link: if want reply and report, give the http links - @param undelete_link: http link to delete the message - @param delete_link: http link to delete the message - @param unreport_link: http link to unreport the comment - @param recID: recID where the comment is posted - @return: html table of review - """ - from invenio.legacy.search_engine import guess_primary_collection_of_a_record - # load the right message language - _ = gettext_set_language(ln) - - if star_score > 0: - star_score_img = 'stars-' + str(star_score) + '-0.png' - else: - star_score_img = 'stars-0-0.png' - - out = "" - - date_creation = convert_datetext_to_dategui(date_creation, ln=ln) - reviewed_label = _("Reviewed by %(x_nickname)s on %(x_date)s") % {'x_nickname': nickname, 'x_date':date_creation} - ## FIX - nb_votes_yes = str(nb_votes_yes) - nb_votes_total = str(nb_votes_total) - useful_label = _("%(x_nb_people)s out of %(x_nb_total)s people found this review useful") % {'x_nb_people': nb_votes_yes, - 'x_nb_total': nb_votes_total} - links = '' - _body = '' - if body != '': - _body = ''' - <blockquote> -%s - </blockquote>''' % email_quoted_txt2html(body, linebreak_html='') - - # Check if user is a comment moderator - record_primary_collection = guess_primary_collection_of_a_record(recID) - user_info = collect_user_info(req) - (auth_code, auth_msg) = acc_authorize_action(user_info, 'moderatecomments', collection=record_primary_collection) - if status in ['dm', 'da'] and not admin_p: - if not auth_code: - if status == 'dm': - _body = '<div class="webcomment_deleted_review_message">(Review deleted by moderator) - not visible for users<br /><br />' +\ - _body + '</div>' - else: - _body = '<div class="webcomment_deleted_review_message">(Review deleted by author) - not visible for users<br /><br />' +\ - _body + '</div>' - links = '<a class="webcomment_deleted_review_undelete" href="' + undelete_link + '">' + _("Undelete review") + '</a>' - else: - if status == 'dm': - _body = '<div class="webcomment_deleted_review_message">Review deleted by moderator</div>' - else: - _body = '<div class="webcomment_deleted_review_message">Review deleted by author</div>' - links = '' - else: - if not auth_code: - links += '<a class="webcomment_review_delete" href="' + delete_links['mod'] +'">' + _("Delete review") + '</a>' - - if nb_reports >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN: - if not auth_code: - _body = '<div class="webcomment_review_pending_approval_message">(Review reported. Pending approval) - not visible for users<br /><br />' + _body + '</div>' - links += ' | ' - links += '<a class="webcomment_reported_review_unreport" href="' + unreport_link +'">' + _("Unreport review") + '</a>' - else: - _body = '<div class="webcomment_review_pending_approval_message">This review is pending approval due to user reports.</div>' - links = '' - - out += ''' -<div class="webcomment_review_box"> - <div class="webcomment_review_box_inner"> - <img src="%(baseurl)s/img/%(star_score_img)s" alt="%(star_score)s/> - <div class="webcomment_review_title">%(title)s</div> - <div class="webcomment_review_label_reviewed">%(reviewed_label)s</div> - <div class="webcomment_review_label_useful">%(useful_label)s</div> - %(body)s - </div> -</div> -%(abuse)s''' % {'baseurl' : CFG_BASE_URL, - 'star_score_img': star_score_img, - 'star_score' : star_score, - 'title' : cgi.escape(title), - 'reviewed_label': reviewed_label, - 'useful_label' : useful_label, - 'body' : _body, - 'abuse' : links - } - return out - - def tmpl_get_comments(self, req, recID, ln, - nb_per_page, page, nb_pages, - display_order, display_since, - CFG_WEBCOMMENT_ALLOW_REVIEWS, - comments, total_nb_comments, - avg_score, - warnings, - border=0, reviews=0, - total_nb_reviews=0, - nickname='', uid=-1, note='',score=5, - can_send_comments=False, - can_attach_files=False, - user_is_subscribed_to_discussion=False, - user_can_unsubscribe_from_discussion=False, - display_comment_rounds=None): - """ - Get table of all comments - @param recID: record id - @param ln: language - @param nb_per_page: number of results per page - @param page: page number - @param display_order: hh = highest helpful score, review only - lh = lowest helpful score, review only - hs = highest star score, review only - ls = lowest star score, review only - od = oldest date - nd = newest date - @param display_since: all= no filtering by date - nd = n days ago - nw = n weeks ago - nm = n months ago - ny = n years ago - where n is a single digit integer between 0 and 9 - @param CFG_WEBCOMMENT_ALLOW_REVIEWS: is ranking enable, get from config.py/CFG_WEBCOMMENT_ALLOW_REVIEWS - @param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks - @param total_nb_comments: total number of comments for this record - @param avg_score: average score of reviews for this record - @param warnings: list of warning tuples (warning_text, warning_color) - @param border: boolean, active if want to show border around each comment/review - @param reviews: boolean, enabled for reviews, disabled for comments - @param can_send_comments: boolean, if user can send comments or not - @param can_attach_files: boolean, if user can attach file to comment or not - @param user_is_subscribed_to_discussion: True if user already receives new comments by email - @param user_can_unsubscribe_from_discussion: True is user is allowed to unsubscribe from discussion - """ - # load the right message language - _ = gettext_set_language(ln) - - # CERN hack begins: display full ATLAS user name. Check further below too. - current_user_fullname = "" - override_nickname_p = False - if CFG_CERN_SITE: - from invenio.legacy.search_engine import get_all_collections_of_a_record - user_info = collect_user_info(uid) - if 'atlas-readaccess-active-members [CERN]' in user_info['group']: - # An ATLAS member is never anonymous to its colleagues - # when commenting inside ATLAS collections - recid_collections = get_all_collections_of_a_record(recID) - if 'ATLAS' in str(recid_collections): - override_nickname_p = True - current_user_fullname = user_info.get('external_fullname', '') - # CERN hack ends - - # naming data fields of comments - if reviews: - c_nickname = 0 - c_user_id = 1 - c_date_creation = 2 - c_body = 3 - c_status = 4 - c_nb_reports = 5 - c_nb_votes_yes = 6 - c_nb_votes_total = 7 - c_star_score = 8 - c_title = 9 - c_id = 10 - c_round_name = 11 - c_restriction = 12 - reply_to = 13 - c_visibility = 14 - discussion = 'reviews' - comments_link = '<a href="%s/%s/%s/comments/">%s</a> (%i)' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, _('Comments'), total_nb_comments) - reviews_link = '<strong>%s (%i)</strong>' % (_('Reviews'), total_nb_reviews) - add_comment_or_review = self.tmpl_add_comment_form_with_ranking(recID, uid, current_user_fullname or nickname, ln, '', score, note, warnings, show_title_p=True, can_attach_files=can_attach_files) - else: - c_nickname = 0 - c_user_id = 1 - c_date_creation = 2 - c_body = 3 - c_status = 4 - c_nb_reports = 5 - c_id = 6 - c_round_name = 7 - c_restriction = 8 - reply_to = 9 - c_visibility = 10 - discussion = 'comments' - comments_link = '<strong>%s (%i)</strong>' % (_('Comments'), total_nb_comments) - reviews_link = '<a href="%s/%s/%s/reviews/">%s</a> (%i)' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, _('Reviews'), total_nb_reviews) - add_comment_or_review = self.tmpl_add_comment_form(recID, uid, nickname, ln, note, warnings, can_attach_files=can_attach_files, user_is_subscribed_to_discussion=user_is_subscribed_to_discussion) - - # voting links - useful_dict = { 'siteurl' : CFG_SITE_URL, - 'CFG_SITE_RECORD' : CFG_SITE_RECORD, - 'recID' : recID, - 'ln' : ln, - 'do' : display_order, - 'ds' : display_since, - 'nb' : nb_per_page, - 'p' : page, - 'reviews' : reviews, - 'discussion' : discussion - } - useful_yes = '<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/vote?ln=%(ln)s&comid=%%(comid)s&com_value=1&do=%(do)s&ds=%(ds)s&nb=%(nb)s&p=%(p)s&referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/display">' + _("Yes") + '</a>' - useful_yes %= useful_dict - useful_no = '<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/vote?ln=%(ln)s&comid=%%(comid)s&com_value=-1&do=%(do)s&ds=%(ds)s&nb=%(nb)s&p=%(p)s&referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/display">' + _("No") + '</a>' - useful_no %= useful_dict - warnings = self.tmpl_warnings(warnings, ln) - - link_dic = { 'siteurl' : CFG_SITE_URL, - 'CFG_SITE_RECORD' : CFG_SITE_RECORD, - 'module' : 'comments', - 'function' : 'index', - 'discussion': discussion, - 'arguments' : 'do=%s&ds=%s&nb=%s' % (display_order, display_since, nb_per_page), - 'arg_page' : '&p=%s' % page, - 'page' : page, - 'rec_id' : recID} - - if not req: - req = None - ## comments table - comments_rows = '' - last_comment_round_name = None - comment_round_names = [comment[0] for comment in comments] - if comment_round_names: - last_comment_round_name = comment_round_names[-1] - - for comment_round_name, comments_list in comments: - comment_round_style = "display:none;" - comment_round_is_open = False - - if comment_round_name in display_comment_rounds: - comment_round_is_open = True - comment_round_style = "" - comments_rows += '<div id="cmtRound%s" class="cmtround">' % (comment_round_name) - if not comment_round_is_open and \ - (comment_round_name or len(comment_round_names) > 1): - new_cmtgrp = list(display_comment_rounds) - new_cmtgrp.append(comment_round_name) - comments_rows += '''<img src="/img/right-trans.gif" id="cmtarrowiconright%(grp_id)s" alt="Open group" /><img src="/img/down-trans.gif" id="cmtarrowicondown%(grp_id)s" alt="Close group" style="display:none" /> - <a class="cmtgrpswitch" name="cmtgrpLink%(grp_id)s" onclick="var cmtarrowicondown=document.getElementById('cmtarrowicondown%(grp_id)s');var cmtarrowiconright=document.getElementById('cmtarrowiconright%(grp_id)s');var subgrp=document.getElementById('cmtSubRound%(grp_id)s');if (subgrp.style.display==''){subgrp.style.display='none';cmtarrowiconright.style.display='';cmtarrowicondown.style.display='none';}else{subgrp.style.display='';cmtarrowiconright.style.display='none';cmtarrowicondown.style.display='';};return false;"''' % {'grp_id': comment_round_name} - comments_rows += 'href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&%(arg_page)s' % link_dic - comments_rows += '&' + '&'.join(["cmtgrp=" + grp for grp in new_cmtgrp if grp != 'none']) + \ - '#cmtgrpLink%s' % (comment_round_name) + '\">' - comments_rows += _('%(x_nb)i comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name} + "</a><br/>" - elif comment_round_name or len(comment_round_names) > 1: - new_cmtgrp = list(display_comment_rounds) - new_cmtgrp.remove(comment_round_name) - - comments_rows += '''<img src="/img/right-trans.gif" id="cmtarrowiconright%(grp_id)s" alt="Open group" style="display:none" /><img src="/img/down-trans.gif" id="cmtarrowicondown%(grp_id)s" alt="Close group" /> - <a class="cmtgrpswitch" name="cmtgrpLink%(grp_id)s" onclick="var cmtarrowicondown=document.getElementById('cmtarrowicondown%(grp_id)s');var cmtarrowiconright=document.getElementById('cmtarrowiconright%(grp_id)s');var subgrp=document.getElementById('cmtSubRound%(grp_id)s');if (subgrp.style.display==''){subgrp.style.display='none';cmtarrowiconright.style.display='';cmtarrowicondown.style.display='none';}else{subgrp.style.display='';cmtarrowiconright.style.display='none';cmtarrowicondown.style.display='';};return false;"''' % {'grp_id': comment_round_name} - comments_rows += 'href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&%(arg_page)s' % link_dic - comments_rows += '&' + ('&'.join(["cmtgrp=" + grp for grp in new_cmtgrp if grp != 'none']) or 'cmtgrp=none' ) + \ - '#cmtgrpLink%s' % (comment_round_name) + '\">' - comments_rows += _('%(x_nb)i comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name}+ "</a><br/>" - comments_rows += '<div id="cmtSubRound%s" class="cmtsubround" style="%s">' % (comment_round_name, - comment_round_style) - comments_rows += ''' - <script type='text/javascript'>//<![CDATA[ - function toggle_visibility(this_link, comid, duration) { - if (duration == null) duration = 0; - var isVisible = $('#collapsible_content_' + comid).is(':visible'); - $('#collapsible_content_' + comid).toggle(duration); - $('#collapsible_ctr_' + comid).toggleClass('webcomment_collapse_ctr_down'); - $('#collapsible_ctr_' + comid).toggleClass('webcomment_collapse_ctr_right'); - if (isVisible){ - $('#collapsible_ctr_' + comid).attr('title', '%(open_label)s'); - $('#collapsible_ctr_' + comid + ' > span').html('%(open_label)s'); - } else { - $('#collapsible_ctr_' + comid).attr('title', '%(close_label)s'); - $('#collapsible_ctr_' + comid + ' > span').html('%(close_label)s'); - } - $.ajax({ - type: 'POST', - url: '%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/toggle', - data: {'comid': comid, 'ln': '%(ln)s', 'collapse': isVisible && 1 || 0} - }); - /* Replace our link with a jump to the adequate, in case needed - (default link is for non-Javascript user) */ - this_link.href = "#C" + comid - /* Find out if after closing comment we shall scroll a bit to the top, - i.e. go back to main anchor of the comment that we have just set */ - var top = $(window).scrollTop(); - if ($(window).scrollTop() >= $("#C" + comid).offset().top) { - // Our comment is now above the window: scroll to it - return true; - } - return false; - } - //]]></script> - ''' % {'siteurl': CFG_SITE_URL, - 'recID': recID, - 'ln': ln, - 'CFG_SITE_RECORD': CFG_SITE_RECORD, - 'open_label': _("Open"), - 'close_label': _("Close")} - thread_history = [0] - previous_depth = 0 - for comment in comments_list: - if comment[reply_to] not in thread_history: - # Going one level down in the thread - thread_history.append(comment[reply_to]) - depth = thread_history.index(comment[reply_to]) - else: - depth = thread_history.index(comment[reply_to]) - thread_history = thread_history[:depth + 1] - - if previous_depth > depth: - comments_rows += ("""</div>""" * (previous_depth-depth)) - - if previous_depth < depth: - comments_rows += ("""<div class="webcomment_thread_block">""" * (depth-previous_depth)) - - previous_depth = depth - - # CERN hack begins: display full ATLAS user name. - comment_user_fullname = "" - if CFG_CERN_SITE and override_nickname_p: - comment_user_fullname = get_email(comment[c_user_id]) - # CERN hack ends - - if comment[c_nickname]: - _nickname = comment[c_nickname] - display = _nickname - else: - (uid, _nickname, display) = get_user_info(comment[c_user_id]) - messaging_link = self.create_messaging_link(_nickname, comment_user_fullname or display, ln) - from invenio.modules.comments.api import get_attached_files # FIXME - files = get_attached_files(recID, comment[c_id]) - # do NOT delete the HTML comment below. It is used for parsing... (I plead unguilty!) - comments_rows += """ - <!-- start comment row --> - <div>""" - delete_links = {} - if not reviews: - report_link = '%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/report?ln=%(ln)s&comid=%%(comid)s&do=%(do)s&ds=%(ds)s&nb=%(nb)s&p=%(p)s&referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/display' % useful_dict % {'comid':comment[c_id]} - reply_link = '%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/add?ln=%(ln)s&action=REPLY&comid=%%(comid)s' % useful_dict % {'comid':comment[c_id]} - delete_links['mod'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_mod?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - delete_links['auth'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_auth?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - undelete_link = "%s/admin/webcomment/webcommentadmin.py/undel_com?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - unreport_link = "%s/admin/webcomment/webcommentadmin.py/unreport_com?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - comments_rows += self.tmpl_get_comment_without_ranking(req, ln, messaging_link, comment[c_user_id], comment[c_date_creation], comment[c_body], comment[c_status], comment[c_nb_reports], reply_link, report_link, undelete_link, delete_links, unreport_link, recID, comment[c_id], files, comment[c_visibility]) - else: - report_link = '%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/report?ln=%(ln)s&comid=%%(comid)s&do=%(do)s&ds=%(ds)s&nb=%(nb)s&p=%(p)s&referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/display' % useful_dict % {'comid': comment[c_id]} - delete_links['mod'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_mod?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - delete_links['auth'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_auth?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - undelete_link = "%s/admin/webcomment/webcommentadmin.py/undel_com?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - unreport_link = "%s/admin/webcomment/webcommentadmin.py/unreport_com?ln=%s&id=%s" % (CFG_SITE_URL, ln, comment[c_id]) - comments_rows += self.tmpl_get_comment_with_ranking(req, ln, messaging_link, comment[c_user_id], comment[c_date_creation], comment[c_body], comment[c_status], comment[c_nb_reports], comment[c_nb_votes_total], comment[c_nb_votes_yes], comment[c_star_score], comment[c_title], report_link, delete_links, undelete_link, unreport_link, recID) - helpful_label = _("Was this review helpful?") - report_abuse_label = "(" + _("Report abuse") + ")" - yes_no_separator = '<td> / </td>' - if comment[c_nb_reports] >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN or comment[c_status] in ['dm', 'da']: - report_abuse_label = "" - helpful_label = "" - useful_yes = "" - useful_no = "" - yes_no_separator = "" - comments_rows += """ - <table> - <tr> - <td>%(helpful_label)s %(tab)s</td> - <td> %(yes)s </td> - %(yes_no_separator)s - <td> %(no)s </td> - <td class="reportabuse">%(tab)s%(tab)s<a href="%(report)s">%(report_abuse_label)s</a></td> - </tr> - </table>""" \ - % {'helpful_label': helpful_label, - 'yes' : useful_yes % {'comid':comment[c_id]}, - 'yes_no_separator': yes_no_separator, - 'no' : useful_no % {'comid':comment[c_id]}, - 'report' : report_link % {'comid':comment[c_id]}, - 'report_abuse_label': comment[c_nb_reports] >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN and '' or report_abuse_label, - 'tab' : ' '*2} - # do NOT remove HTML comment below. It is used for parsing... - comments_rows += """ - </div> - <!-- end comment row -->""" - comments_rows += '</div></div>' - - ## page links - page_links = '' - # Previous - if page != 1: - link_dic['arg_page'] = 'p=%s' % (page - 1) - page_links += '<a href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&%(arg_page)s\"><<</a> ' % link_dic - else: - page_links += ' %s ' % (' '*(len(_('Previous'))+7)) - # Page Numbers - for i in range(1, nb_pages+1): - link_dic['arg_page'] = 'p=%s' % i - link_dic['page'] = '%s' % i - if i != page: - page_links += ''' - <a href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&%(arg_page)s\">%(page)s</a> ''' % link_dic - else: - page_links += ''' <b>%s</b> ''' % i - # Next - if page != nb_pages: - link_dic['arg_page'] = 'p=%s' % (page + 1) - page_links += ''' - <a href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&%(arg_page)s\">>></a> ''' % link_dic - else: - page_links += '%s' % (' '*(len(_('Next'))+7)) - - ## stuff for ranking if enabled - if reviews: - if avg_score > 0: - avg_score_img = 'stars-' + str(avg_score).split('.')[0] + '-' + str(avg_score).split('.')[1] + '.png' - else: - avg_score_img = "stars-0-0.png" - ranking_average = '<br /><b>' - ranking_average += _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \ - {'x_nb_score': '</b><img src="' + CFG_SITE_URL + '/img/' + avg_score_img + '" alt="' + str(avg_score) + '" />', - 'x_nb_reviews': str(total_nb_reviews)} - ranking_average += '<br />' - else: - ranking_average = "" - - write_button_link = '''%s/%s/%s/%s/add''' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, discussion) - write_button_form = '<input type="hidden" name="ln" value="%s"/>' - write_button_form = self.createhiddenform( - action=write_button_link, - method="get", - text=write_button_form, - button=reviews and _('Write a review') or _('Write a comment') - ) - - if reviews: - total_label = _("There is a total of %(x_num)s reviews", x_num=total_nb_comments) - else: - total_label = _("There is a total of %(x_num)s comments", x_num=total_nb_comments) - #total_label %= total_nb_comments - - review_or_comment_first = "" - if not reviews and total_nb_comments == 0 and can_send_comments: - review_or_comment_first = \ - "<p>" + \ - _("Start a discussion about any aspect of this document.") + \ - "</p>" - elif reviews and total_nb_reviews == 0 and can_send_comments: - review_or_comment_first = \ - "<p>" + \ - _("Be the first to review this document.") + \ - "</p>" - - subscription_link = "" - if not reviews: - if not user_is_subscribed_to_discussion: - subscription_link = \ - '<p class="comment-subscribe">' + \ - '<img src="%s/img/mail-icon-12x8.gif" border="0" alt="" />' % CFG_SITE_URL + \ - ' ' + \ - '<strong>' + \ - create_html_link( - urlbase=CFG_SITE_URL + - '/' + - CFG_SITE_RECORD + - '/' + - str(recID) + - '/comments/subscribe', - urlargd={}, - link_label=_('Subscribe')) + \ - '</strong>' + \ - ' to this discussion. You will then receive all new comments by email.' + \ - '</p>' - elif user_can_unsubscribe_from_discussion: - subscription_link = \ - '<p class="comment-subscribe">' + \ - '<img src="%s/img/mail-icon-12x8.gif" border="0" alt="" />' % CFG_SITE_URL + \ - ' ' + \ - '<strong>' + \ - create_html_link( - urlbase=CFG_SITE_URL + - '/' + - CFG_SITE_RECORD + - '/' + - str(recID) + - '/comments/unsubscribe', - urlargd={}, - link_label=_('Unsubscribe')) + \ - '</strong>' + \ - ' from this discussion. You will no longer receive emails about new comments.' + \ - '</p>' - - # do NOT remove the HTML comments below. Used for parsing - body = ''' -%(comments_and_review_tabs)s -%(subscription_link_before)s -<!-- start comments table --> -<div class="webcomment_comment_table"> - %(comments_rows)s -</div> -<!-- end comments table --> -%(review_or_comment_first)s -%(subscription_link_after)s -''' % { - 'record_label': _("Record"), - 'back_label': _("Back to search results"), - 'total_label': total_label, - 'write_button_form': write_button_form, - 'write_button_form_again': - total_nb_comments > 3 and - write_button_form or - "", - 'comments_rows': comments_rows, - 'total_nb_comments': total_nb_comments, - 'comments_or_reviews': reviews and _('review') or _('comment'), - 'comments_or_reviews_title': - reviews and - _('Review') or - _('Comment'), - 'siteurl': CFG_SITE_URL, - 'module': "comments", - 'recid': recID, - 'ln': ln, - # 'border': border, - 'ranking_avg': ranking_average, - 'comments_and_review_tabs': - CFG_WEBCOMMENT_ALLOW_REVIEWS and - CFG_WEBCOMMENT_ALLOW_COMMENTS and - '<p>%s | %s</p>' % (comments_link, reviews_link) or - "", - 'review_or_comment_first': review_or_comment_first, - 'subscription_link_before': - not reviews and - total_nb_comments != 0 and - subscription_link or "", - 'subscription_link_after': subscription_link - } - - # form is not currently used. reserved for an eventual purpose - #form = """ - # Display <select name="nb" size="1"> per page - # <option value="all">All</option> - # <option value="10">10</option> - # <option value="25">20</option> - # <option value="50">50</option> - # <option value="100" selected="selected">100</option> - # </select> - # comments per page that are <select name="ds" size="1"> - # <option value="all" selected="selected">Any age</option> - # <option value="1d">1 day old</option> - # <option value="3d">3 days old</option> - # <option value="1w">1 week old</option> - # <option value="2w">2 weeks old</option> - # <option value="1m">1 month old</option> - # <option value="3m">3 months old</option> - # <option value="6m">6 months old</option> - # <option value="1y">1 year old</option> - # </select> - # and sorted by <select name="do" size="1"> - # <option value="od" selected="selected">Oldest first</option> - # <option value="nd">Newest first</option> - # %s - # </select> - # """ % \ - # (reviews==1 and ''' - # <option value=\"hh\">most helpful</option> - # <option value=\"lh\">least helpful</option> - # <option value=\"hs\">highest star ranking</option> - # <option value=\"ls\">lowest star ranking</option> - # </select>''' or ''' - # </select>''') - # - #form_link = "%(siteurl)s/%(module)s/%(function)s" % link_dic - #form = self.createhiddenform(action=form_link, method="get", text=form, button='Go', recid=recID, p=1) - pages = """ -<div> -%(v_label)s %(comments_or_reviews)s %(results_nb_lower)s-%(results_nb_higher)s <br /> -%(page_links)s -</div> -""" % \ - {'v_label': _("Viewing"), - 'page_links': _("Page:") + page_links , - 'comments_or_reviews': reviews and _('review') or _('comment'), - 'results_nb_lower': len(comments)>0 and ((page-1) * nb_per_page)+1 or 0, - 'results_nb_higher': page == nb_pages and (((page-1) * nb_per_page) + len(comments)) or (page * nb_per_page)} - - if nb_pages > 1: - #body = warnings + body + form + pages - body = warnings + body + pages - else: - body = warnings + body - - if can_send_comments: - body += add_comment_or_review - else: - body += '<br/><em>' + _("You are not authorized to comment or review.") + '</em>' - - return '<div class="webcomment_container">' + body + '</div>' - - def create_messaging_link(self, to, display_name, ln=CFG_SITE_LANG): - """prints a link to the messaging system""" - link = "%s/yourmessages/write?msg_to=%s&ln=%s" % (CFG_SITE_URL, to, ln) - if to: - return '<a href="%s" class="maillink">%s</a>' % (link, display_name) - else: - return display_name - - def createhiddenform(self, action="", method="get", text="", button="confirm", cnfrm='', **hidden): - """ - create select with hidden values and submit button - @param action: name of the action to perform on submit - @param method: 'get' or 'post' - @param text: additional text, can also be used to add non hidden input - @param button: value/caption on the submit button - @param cnfrm: if given, must check checkbox to confirm - @param **hidden: dictionary with name=value pairs for hidden input - @return: html form - """ - - output = """ -<form action="%s" method="%s">""" % (action, method.lower().strip() in ['get', 'post'] and method or 'get') - output += """ - <table style="width:90%"> - <tr> - <td style="vertical-align: top"> -""" - output += text + '\n' - if cnfrm: - output += """ - <input type="checkbox" name="confirm" value="1" />""" - for key in hidden.keys(): - if type(hidden[key]) is list: - for value in hidden[key]: - output += """ - <input type="hidden" name="%s" value="%s" />""" % (key, value) - else: - output += """ - <input type="hidden" name="%s" value="%s" />""" % (key, hidden[key]) - output += """ - </td> - </tr> - <tr> - <td>""" - output += """ - <input class="adminbutton" type="submit" value="%s" />""" % (button, ) - output += """ - </td> - </tr> - </table> -</form>""" - return output - - def create_write_comment_hiddenform(self, action="", method="get", text="", button="confirm", cnfrm='', - enctype='', form_id=None, form_name=None, **hidden): - """ - create select with hidden values and submit button - @param action: name of the action to perform on submit - @param method: 'get' or 'post' - @param text: additional text, can also be used to add non hidden input - @param button: value/caption on the submit button - @param cnfrm: if given, must check checkbox to confirm - @param form_id: HTML 'id' attribute of the form tag - @param form_name: HTML 'name' attribute of the form tag - @param **hidden: dictionary with name=value pairs for hidden input - @return: html form - """ - enctype_attr = '' - if enctype: - enctype_attr = 'enctype="%s"' % enctype - - output = """ -<form action="%s" method="%s" %s%s%s>""" % \ - (action, method.lower().strip() in ['get', 'post'] and method or 'get', - enctype_attr, form_name and ' name="%s"' % form_name or '', - form_id and ' id="%s"' % form_id or '') - if cnfrm: - output += """ - <input type="checkbox" name="confirm" value="1" />""" - for key in hidden.keys(): - if type(hidden[key]) is list: - for value in hidden[key]: - output += """ - <input type="hidden" name="%s" value="%s" />""" % (key, value) - else: - output += """ - <input type="hidden" name="%s" value="%s" />""" % (key, hidden[key]) - output += text + '\n' - output += """ -</form>""" - return output - - def tmpl_warnings(self, warnings=[], ln=CFG_SITE_LANG): - """ - Display len(warnings) warning fields - @param warnings: list of warning tuples (warning_text, warning_color) - @param ln=language - @return: html output - """ - if type(warnings) is not list: - warnings = [warnings] - warningbox = "" - if warnings: - for i in range(len(warnings)): - warning_text = warnings[i][0] - warning_color = warnings[i][1] - if warning_color == 'green': - span_class = 'exampleleader' - else: - span_class = 'important' - warningbox += ''' - <span class="%(span_class)s">%(warning)s</span><br />''' % \ - { 'span_class' : span_class, - 'warning' : warning_text } - return warningbox - else: - return "" - - def tmpl_error(self, error, ln=CFG_SITE_LANG): - """ - Display error - @param error: string - @param ln=language - @return: html output - """ - _ = gettext_set_language(ln) - errorbox = "" - if error != "": - errorbox = "<div class=\"errorbox\">\n <b>Error:</b>\n" - errorbox += " <p>" - errorbox += error + " </p>" - errorbox += "</div><br />\n" - return errorbox - - def tmpl_add_comment_form(self, recID, uid, nickname, ln, msg, - warnings, textual_msg=None, can_attach_files=False, - user_is_subscribed_to_discussion=False, reply_to=None): - """ - Add form for comments - @param recID: record id - @param uid: user id - @param ln: language - @param msg: comment body contents for when refreshing due to - warning, or when replying to a comment - @param textual_msg: same as 'msg', but contains the textual - version in case user cannot display CKeditor - @param warnings: list of warning tuples (warning_text, warning_color) - @param can_attach_files: if user can upload attach file to record or not - @param user_is_subscribed_to_discussion: True if user already receives new comments by email - @param reply_to: the ID of the comment we are replying to. None if not replying - @return html add comment form - """ - _ = gettext_set_language(ln) - link_dic = { 'siteurl' : CFG_SITE_URL, - 'CFG_SITE_RECORD' : CFG_SITE_RECORD, - 'module' : 'comments', - 'function' : 'add', - 'arguments' : 'ln=%s&action=%s' % (ln, 'SUBMIT'), - 'recID' : recID} - if textual_msg is None: - textual_msg = msg - - # FIXME a cleaner handling of nicknames is needed. - if not nickname: - (uid, nickname, display) = get_user_info(uid) - if nickname: - note = _("Note: Your nickname, %(nick)s, will be displayed as author of this comment.", - nick='<i>' + nickname + '</i>') - else: - (uid, nickname, display) = get_user_info(uid) - link = '<a href="%s/youraccount/edit">' % CFG_SITE_SECURE_URL - note = _("Note: you have not %(x_url_open)sdefined your nickname%(x_url_close)s. %(x_nickname)s will be displayed as the author of this comment.") % \ - {'x_url_open': link, - 'x_url_close': '</a>', - 'x_nickname': ' <br /><i>' + display + '</i>'} - - if not CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR: - note += '<br />' + ' '*10 + cgi.escape('You can use some HTML tags: <a href>, <strong>, <blockquote>, <br />, <p>, <em>, <ul>, <li>, <b>, <i>') - #from invenio.legacy.search_engine import print_record - #record_details = print_record(recID=recID, format='hb', ln=ln) - - warnings = self.tmpl_warnings(warnings, ln) - - # Prepare file upload settings. We must enable file upload in - # the ckeditor + a simple file upload interface (independant from editor) - file_upload_url = None - simple_attach_file_interface = '' - if isGuestUser(uid): - simple_attach_file_interface = "<small><em>%s</em></small><br/>" % _("Once logged in, authorized users can also attach files.") - if can_attach_files: - # Note that files can be uploaded only when user is logged in - #file_upload_url = '%s/%s/%i/comments/attachments/put' % \ - # (CFG_SITE_URL, CFG_SITE_RECORD, recID) - simple_attach_file_interface = ''' - <div id="uploadcommentattachmentsinterface"> - <small>%(attach_msg)s: <em>(%(nb_files_limit_msg)s. %(file_size_limit_msg)s)</em></small><br /> - <input class="multi max-%(CFG_WEBCOMMENT_MAX_ATTACHED_FILES)s" type="file" name="commentattachment[]"/><br /> - <noscript> - <input type="file" name="commentattachment[]" /><br /> - </noscript> - </div> - ''' % \ - {'CFG_WEBCOMMENT_MAX_ATTACHED_FILES': CFG_WEBCOMMENT_MAX_ATTACHED_FILES, - 'attach_msg': CFG_WEBCOMMENT_MAX_ATTACHED_FILES == 1 and _("Optionally, attach a file to this comment") or \ - _("Optionally, attach files to this comment"), - 'nb_files_limit_msg': _("Max one file") and CFG_WEBCOMMENT_MAX_ATTACHED_FILES == 1 or \ - _("Max %(maxfiles)i files", maxfiles=CFG_WEBCOMMENT_MAX_ATTACHED_FILES), - 'file_size_limit_msg': CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE > 0 and _("Max %(x_nb_bytes)s per file") % {'x_nb_bytes': (CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE < 1024*1024 and (str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/1024) + 'KB') or (str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/(1024*1024)) + 'MB'))} or ''} - - editor = get_html_text_editor(name='msg', - content=msg, - textual_content=textual_msg, - width='100%', - height='400px', - enabled=CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR, - file_upload_url=file_upload_url, - toolbar_set = "WebComment", - ln=ln) - - subscribe_to_discussion = '' - if not user_is_subscribed_to_discussion: - # Offer to subscribe to discussion - subscribe_to_discussion = '<small><input type="checkbox" name="subscribe" id="subscribe"/><label for="subscribe">%s</label></small>' % _("Send me an email when a new comment is posted") - - form = """<div id="comment-write"><h2>%(add_comment)s</h2> - -%(editor)s -<br /> -%(simple_attach_file_interface)s - <span class="reportabuse">%(note)s</span> - <div class="submit-area"> - %(subscribe_to_discussion)s<br /> - <input class="adminbutton" type="submit" value="Add comment" onclick="user_must_confirm_before_leaving_page = false;return true;"/> - %(reply_to)s - </div> -</div> - """ % {'note': note, - 'record_label': _("Article") + ":", - 'comment_label': _("Comment") + ":", - 'add_comment': _('Add comment'), - 'editor': editor, - 'subscribe_to_discussion': subscribe_to_discussion, - 'reply_to': reply_to and '<input type="hidden" name="comid" value="%s"/>' % reply_to or '', - 'simple_attach_file_interface': simple_attach_file_interface} - form_link = "%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/%(function)s?%(arguments)s" % link_dic - form = self.create_write_comment_hiddenform(action=form_link, method="post", text=form, button='Add comment', - enctype='multipart/form-data', form_id='cmtForm', - form_name='cmtForm') - - return warnings + form + self.tmpl_page_do_not_leave_comment_page_js(ln=ln) - - def tmpl_add_comment_form_with_ranking(self, recID, uid, nickname, ln, msg, score, note, - warnings, textual_msg=None, show_title_p=False, - can_attach_files=False): - """ - Add form for reviews - @param recID: record id - @param uid: user id - @param ln: language - @param msg: comment body contents for when refreshing due to warning - @param textual_msg: the textual version of 'msg' when user cannot display Ckeditor - @param score: review score - @param note: review title - @param warnings: list of warning tuples (warning_text, warning_color) - @param show_title_p: if True, prefix the form with "Add Review" as title - @param can_attach_files: if user can upload attach file to record or not - @return: html add review form - """ - _ = gettext_set_language(ln) - link_dic = { 'siteurl' : CFG_SITE_URL, - 'CFG_SITE_RECORD' : CFG_SITE_RECORD, - 'module' : 'comments', - 'function' : 'add', - 'arguments' : 'ln=%s&action=%s' % (ln, 'SUBMIT'), - 'recID' : recID} - warnings = self.tmpl_warnings(warnings, ln) - - if textual_msg is None: - textual_msg = msg - - #from search_engine import print_record - #record_details = print_record(recID=recID, format='hb', ln=ln) - if nickname: - note_label = _("Note: Your nickname, %(x_name)s, will be displayed as the author of this review.", - x_name=('<i>' + nickname + '</i>')) - else: - (uid, nickname, display) = get_user_info(uid) - link = '<a href="%s/youraccount/edit">' % CFG_SITE_SECURE_URL - note_label = _("Note: you have not %(x_url_open)sdefined your nickname%(x_url_close)s. %(x_nickname)s will be displayed as the author of this comment.") % \ - {'x_url_open': link, - 'x_url_close': '</a>', - 'x_nickname': ' <br /><i>' + display + '</i>'} - - selected0 = '' - selected1 = '' - selected2 = '' - selected3 = '' - selected4 = '' - selected5 = '' - if score == 0: - selected0 = ' selected="selected"' - elif score == 1: - selected1 = ' selected="selected"' - elif score == 2: - selected2 = ' selected="selected"' - elif score == 3: - selected3 = ' selected="selected"' - elif score == 4: - selected4 = ' selected="selected"' - elif score == 5: - selected5 = ' selected="selected"' - -# file_upload_url = None -# if can_attach_files: -# file_upload_url = '%s/%s/%i/comments/attachments/put' % \ -# (CFG_SITE_URL, CFG_SITE_RECORD, recID) - - editor = get_html_text_editor(name='msg', - content=msg, - textual_content=msg, - width='90%', - height='400px', - enabled=CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR, -# file_upload_url=file_upload_url, - toolbar_set = "WebComment", - ln=ln) - form = """%(add_review)s - <table style="width: 100%%"> - <tr> - <td style="padding-bottom: 10px;">%(rate_label)s: - <select name=\"score\" size=\"1\"> - <option value=\"0\"%(selected0)s>-%(select_label)s-</option> - <option value=\"5\"%(selected5)s>***** (best)</option> - <option value=\"4\"%(selected4)s>****</option> - <option value=\"3\"%(selected3)s>***</option> - <option value=\"2\"%(selected2)s>**</option> - <option value=\"1\"%(selected1)s>* (worst)</option> - </select> - </td> - </tr> - <tr> - <td>%(title_label)s:</td> - </tr> - <tr> - <td style="padding-bottom: 10px;"> - <input type="text" name="note" maxlength="250" style="width:90%%" value="%(note)s" /> - </td> - </tr> - <tr> - <td>%(write_label)s:</td> - </tr> - <tr> - <td> - %(editor)s - </td> - </tr> - <tr> - <td class="reportabuse">%(note_label)s</td></tr> - </table> - """ % {'article_label': _('Article'), - 'rate_label': _("Rate this article"), - 'select_label': _("Select a score"), - 'title_label': _("Give a title to your review"), - 'write_label': _("Write your review"), - 'note_label': note_label, - 'note' : note!='' and cgi.escape(note, quote=True) or "", - 'msg' : msg!='' and msg or "", - #'record' : record_details - 'add_review': show_title_p and ('<h2>'+_('Add review')+'</h2>') or '', - 'selected0': selected0, - 'selected1': selected1, - 'selected2': selected2, - 'selected3': selected3, - 'selected4': selected4, - 'selected5': selected5, - 'editor': editor, - } - form_link = "%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/%(function)s?%(arguments)s" % link_dic - form = self.createhiddenform(action=form_link, method="post", text=form, button=_('Add Review')) - return warnings + form - - def tmpl_add_comment_successful(self, recID, ln, reviews, warnings, success): - """ - @param recID: record id - @param ln: language - @return: html page of successfully added comment/review - """ - _ = gettext_set_language(ln) - link_dic = { 'siteurl' : CFG_SITE_URL, - 'CFG_SITE_RECORD' : CFG_SITE_RECORD, - 'module' : 'comments', - 'function' : 'display', - 'arguments' : 'ln=%s&do=od' % ln, - 'recID' : recID, - 'discussion': reviews == 1 and 'reviews' or 'comments'} - link = "%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/%(function)s?%(arguments)s" % link_dic - if warnings: - out = self.tmpl_warnings(warnings, ln) + '<br /><br />' - else: - if reviews: - out = _("Your review was successfully added.") + '<br /><br />' - else: - out = _("Your comment was successfully added.") + '<br /><br />' - link += "#C%s" % success - out += '<a href="%s">' % link - out += _('Back to record') + '</a>' - out += '<br/><br/>' \ - + _('You can also view all the comments you have submitted so far on "%(x_url_open)sYour Comments%(x_url_close)s" page.') % \ - {'x_url_open': '<a target="_blank" href="%(CFG_SITE_URL)s/yourcomments?ln=%(ln)s">' % {'CFG_SITE_URL': CFG_SITE_URL, 'ln': ln}, - 'x_url_close': '</a>'} - return out - - def tmpl_create_multiple_actions_form(self, - form_name="", - form_action="", - method="get", - action_display={}, - action_field_name="", - button_label="", - button_name="", - content="", - **hidden): - """ Creates an HTML form with a multiple choice of actions and a button to select it. - @param form_action: link to the receiver of the formular - @param form_name: name of the HTML formular - @param method: either 'GET' or 'POST' - @param action_display: dictionary of actions. - action is HTML name (name of action) - display is the string provided in the popup - @param action_field_name: html name of action field - @param button_label: what's written on the button - @param button_name: html name of the button - @param content: what's inside te formular - @param **hidden: dictionary of name/value pairs of hidden fields. - """ - output = """ -<form action="%s" method="%s">""" % (form_action, method) - output += """ - <table> - <tr> - <td style="vertical-align: top" colspan="2"> -""" - output += content + '\n' - for key in hidden.keys(): - if type(hidden[key]) is list: - for value in hidden[key]: - output += """ - <input type="hidden" name="%s" value="%s" />""" % (key, value) - else: - output += """ - <input type="hidden" name="%s" value="%s" />""" % (key, hidden[key]) - output += """ - </td> - </tr> - <tr> - <td style="text-align:right;">""" - if type(action_display) is dict and len(action_display.keys()): - output += """ - <select name="%s">""" % action_field_name - for (key, value) in action_display.items(): - output += """ - <option value="%s">%s</option>""" % (key, value) - output += """ - </select>""" - output += """ - </td> - <td style="text-align:left;"> - <input class="adminbutton" type="submit" value="%s" name="%s"/>""" % (button_label, button_name) - output += """ - </td> - </tr> - </table> -</form>""" - return output - - def tmpl_admin_index(self, ln): - """ - Index page - """ - # load the right message language - _ = gettext_set_language(ln) - - out = '<ol>' - if CFG_WEBCOMMENT_ALLOW_COMMENTS or CFG_WEBCOMMENT_ALLOW_REVIEWS: - if CFG_WEBCOMMENT_ALLOW_COMMENTS: - out += '<h3>Comments status</h3>' - out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/hot?ln=%(ln)s&comments=1">%(hot_cmt_label)s</a></li>' % \ - {'siteurl': CFG_SITE_URL, 'ln': ln, 'hot_cmt_label': _("View most commented records")} - out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/latest?ln=%(ln)s&comments=1">%(latest_cmt_label)s</a></li>' % \ - {'siteurl': CFG_SITE_URL, 'ln': ln, 'latest_cmt_label': _("View latest commented records")} - out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&reviews=0">%(reported_cmt_label)s</a></li>' % \ - {'siteurl': CFG_SITE_URL, 'ln': ln, 'reported_cmt_label': _("View all comments reported as abuse")} - if CFG_WEBCOMMENT_ALLOW_REVIEWS: - out += '<h3>Reviews status</h3>' - out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/hot?ln=%(ln)s&comments=0">%(hot_rev_label)s</a></li>' % \ - {'siteurl': CFG_SITE_URL, 'ln': ln, 'hot_rev_label': _("View most reviewed records")} - out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/latest?ln=%(ln)s&comments=0">%(latest_rev_label)s</a></li>' % \ - {'siteurl': CFG_SITE_URL, 'ln': ln, 'latest_rev_label': _("View latest reviewed records")} - out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&reviews=1">%(reported_rev_label)s</a></li>' % \ - {'siteurl': CFG_SITE_URL, 'ln': ln, 'reported_rev_label': _("View all reviews reported as abuse")} - #<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/delete?ln=%(ln)s&comid=-1">%(delete_label)s</a></li> - out +=""" - <h3>General</h3> - <li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/users?ln=%(ln)s">%(view_users)s</a></li> - <li><a href="%(siteurl)s/help/admin/webcomment-admin-guide">%(guide)s</a></li> - """ % {'siteurl' : CFG_SITE_URL, - #'delete_label': _("Delete/Undelete comment(s) or suppress abuse report(s)"), - 'view_users': _("View all users who have been reported"), - 'ln' : ln, - 'guide' : _("Guide")} - else: - out += _("Comments and reviews are disabled") + '<br />' - out += '</ol>' - from invenio.legacy.bibrank.adminlib import addadminbox - return addadminbox('<b>%s</b>'% _("Menu"), [out]) - - def tmpl_admin_delete_form(self, ln, warnings): - """ - Display admin interface to fetch list of records to delete - - @param warnings: list of warning tuples (warning_text, warning_color) - see tmpl_warnings, warning_color is optional - """ - # load the right message language - _ = gettext_set_language(ln) - - warnings = self.tmpl_warnings(warnings, ln) - - out = ''' - <br /> - %s<br /> - <br />'''% _("Please enter the ID of the comment/review so that you can view it before deciding whether to delete it or not") - form = ''' - <table> - <tr> - <td>%s</td> - <td><input type=text name="comid" size="10" maxlength="10" value="" /></td> - </tr> - <tr> - <td><br /></td> - <tr> - </table> - <br /> - %s <br/> - <br /> - <table> - <tr> - <td>%s</td> - <td><input type=text name="recid" size="10" maxlength="10" value="" /></td> - </tr> - <tr> - <td><br /></td> - <tr> - </table> - <br /> - ''' % (_("Comment ID:"), - _("Or enter a record ID to list all the associated comments/reviews:"), - _("Record ID:")) - form_link = "%s/admin/webcomment/webcommentadmin.py/delete?ln=%s" % (CFG_SITE_URL, ln) - form = self.createhiddenform(action=form_link, method="get", text=form, button=_('View Comment')) - return warnings + out + form - - def tmpl_admin_users(self, ln, users_data): - """ - @param users_data: tuple of ct, i.e. (ct, ct, ...) - where ct is a tuple (total_number_reported, total_comments_reported, total_reviews_reported, total_nb_votes_yes_of_reported, - total_nb_votes_total_of_reported, user_id, user_email, user_nickname) - sorted by order of ct having highest total_number_reported - """ - _ = gettext_set_language(ln) - u_reports = 0 - u_comment_reports = 1 - u_reviews_reports = 2 - u_nb_votes_yes = 3 - u_nb_votes_total = 4 - u_uid = 5 - u_email = 6 - u_nickname = 7 - - if not users_data: - return self.tmpl_warnings([(_("There have been no reports so far."), 'green')]) - - user_rows = "" - for utuple in users_data: - com_label = _("View all %(x_name)s reported comments", x_name=utuple[u_comment_reports]) - com_link = '''<a href="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&uid=%s&reviews=0">%s</a><br />''' % \ - (CFG_SITE_URL, ln, utuple[u_uid], com_label) - rev_label = _("View all %(x_name)s reported reviews", x_name=utuple[u_reviews_reports]) - rev_link = '''<a href="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&uid=%s&reviews=1">%s</a>''' % \ - (CFG_SITE_URL, ln, utuple[u_uid], rev_label) - if not utuple[u_nickname]: - user_info = get_user_info(utuple[u_uid]) - nickname = user_info[2] - else: - nickname = utuple[u_nickname] - if CFG_WEBCOMMENT_ALLOW_REVIEWS: - review_row = """ -<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> -<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> -<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>""" - review_row %= (utuple[u_nb_votes_yes], - utuple[u_nb_votes_total] - utuple[u_nb_votes_yes], - utuple[u_nb_votes_total]) - else: - review_row = '' - user_rows += """ -<tr> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(nickname)s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(email)s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(uid)s</td>%(review_row)s - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray; font-weight: bold;">%(reports)s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(com_link)s%(rev_link)s</td> -</tr>""" % { 'nickname' : nickname, - 'email' : utuple[u_email], - 'uid' : utuple[u_uid], - 'reports' : utuple[u_reports], - 'review_row': review_row, - 'siteurl' : CFG_SITE_URL, - 'ln' : ln, - 'com_link' : CFG_WEBCOMMENT_ALLOW_COMMENTS and com_link or "", - 'rev_link' : CFG_WEBCOMMENT_ALLOW_REVIEWS and rev_link or "" - } - - out = "<br />" - out += _("Here is a list, sorted by total number of reports, of all users who have had a comment reported at least once.") - out += """ -<br /> -<br /> -<table class="admin_wvar" style="width: 100%%;"> - <thead> - <tr class="adminheaderleft"> - <th>""" - out += _("Nickname") + '</th>\n' - out += '<th>' + _("Email") + '</th>\n' - out += '<th>' + _("User ID") + '</th>\n' - if CFG_WEBCOMMENT_ALLOW_REVIEWS > 0: - out += '<th>' + _("Number positive votes") + '</th>\n' - out += '<th>' + _("Number negative votes") + '</th>\n' - out += '<th>' + _("Total number votes") + '</th>\n' - out += '<th>' + _("Total number of reports") + '</th>\n' - out += '<th>' + _("View all user's reported comments/reviews") + '</th>\n' - out += """ - </tr> - </thead> - <tbody>%s - </tbody> -</table> - """ % user_rows - return out - - def tmpl_admin_select_comment_checkbox(self, cmt_id): - """ outputs a checkbox named "comidXX" where XX is cmt_id """ - return '<input type="checkbox" name="comid%i" />' % int(cmt_id) - - def tmpl_admin_user_info(self, ln, nickname, uid, email): - """ prepares informations about a user""" - _ = gettext_set_language(ln) - out = """ -%(nickname_label)s: %(messaging)s<br /> -%(uid_label)s: %(uid)i<br /> -%(email_label)s: <a href="mailto:%(email)s">%(email)s</a>""" - out %= {'nickname_label': _("Nickname"), - 'messaging': self.create_messaging_link(uid, nickname, ln), - 'uid_label': _("User ID"), - 'uid': int(uid), - 'email_label': _("Email"), - 'email': email} - return out - - def tmpl_admin_review_info(self, ln, reviews, nb_reports, cmt_id, rec_id, status): - """ outputs information about a review """ - _ = gettext_set_language(ln) - if reviews: - reported_label = _("This review has been reported %(x_num)i times", x_num=int(nb_reports)) - else: - reported_label = _("This comment has been reported %(x_num)i times", x_num=int(nb_reports)) - # reported_label %= int(nb_reports) - out = """ -%(reported_label)s<br /> -<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)i?ln=%(ln)s">%(rec_id_label)s</a><br /> -%(cmt_id_label)s""" - out %= {'reported_label': reported_label, - 'rec_id_label': _("Record") + ' #' + str(rec_id), - 'siteurl': CFG_SITE_URL, - 'CFG_SITE_RECORD' : CFG_SITE_RECORD, - 'rec_id': int(rec_id), - 'cmt_id_label': _("Comment") + ' #' + str(cmt_id), - 'ln': ln} - if status in ['dm', 'da']: - out += '<br /><div style="color:red;">Marked as deleted</div>' - return out - - def tmpl_admin_latest(self, ln, comment_data, comments, error, user_collections, collection): - """ - @param comment_data: same type of tuple as that - which is return by webcommentadminlib.py/query_get_latest i.e. - tuple (nickname, uid, date_creation, body, id) if latest comments or - tuple (nickname, uid, date_creation, body, star_score, id) if latest reviews - """ - _ = gettext_set_language(ln) - - out = """ - <script type='text/javascript'> - function collectionChange() - { - document.collection_form.submit(); - } - </script> - """ - - out += '<form method="get" name="collection_form" action="%s/admin/webcomment/webcommentadmin.py/latest?ln=%s&comments=%s">' % (CFG_SITE_URL, ln, comments) - out += '<input type="hidden" name="ln" value=%s>' % ln - out += '<input type="hidden" name="comments" value=%s>' % comments - out += '<div> Filter by collection: <select name="collection" onchange="javascript:collectionChange();">' - for collection_name in user_collections: - if collection_name == collection: - out += '<option "SELECTED" value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)} - else: - out += '<option value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)} - out += '</select></div></form><br />' - if error == 1: - out += "<i>User is not authorized to view such collection.</i><br />" - return out - elif error == 2: - out += "<i>There are no %s for this collection.</i><br />" % (comments and 'comments' or 'reviews') - return out - - out += """ - <ol> - """ - for (cmt_tuple, meta_data) in comment_data: - bibrec_id = meta_data[3] - content = format_record(bibrec_id, "hs") - if not comments: - out += """ - <li> %(content)s <br/> <span class="moreinfo"> <a class="moreinfo" href=%(comment_url)s> reviewed by %(user)s</a> - (%(stars)s) \"%(body)s\" on <i> %(date)s </i></li> </span> <br/> - """ % {'content': content, - 'comment_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/reviews', - 'user':cmt_tuple[0] , - 'stars': '*' * int(cmt_tuple[4]) , - 'body': cmt_tuple[3][:20] + '...', - 'date': cmt_tuple[2]} - else: - out += """ - <li> %(content)s <br/> <span class="moreinfo"> <a class="moreinfo" href=%(comment_url)s> commented by %(user)s</a>, - \"%(body)s\" on <i> %(date)s </i></li> </span> <br/> - """ % {'content': content, - 'comment_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/comments', - 'user':cmt_tuple[0] , - 'body': cmt_tuple[3][:20] + '...', - 'date': cmt_tuple[2]} - - out += """</ol>""" - return out - - def tmpl_admin_hot(self, ln, comment_data, comments, error, user_collections, collection): - """ - @param comment_data: same type of tuple as that - which is return by webcommentadminlib.py/query_get_hot i.e. - tuple (id_bibrec, date_last_comment, users, count) - """ - _ = gettext_set_language(ln) - - out = """ - <script type='text/javascript'> - function collectionChange() - { - document.collection_form.submit(); - } - </script> - """ - - out += '<form method="get" name="collection_form" action="%s/admin/webcomment/webcommentadmin.py/hot?ln=%s&comments=%s">' % (CFG_SITE_URL, ln, comments) - out += '<input type="hidden" name="ln" value=%s>' % ln - out += '<input type="hidden" name="comments" value=%s>' % comments - out += '<div> Filter by collection: <select name="collection" onchange="javascript:collectionChange();">' - for collection_name in user_collections: - if collection_name == collection: - out += '<option "SELECTED" value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)} - else: - out += '<option value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)} - out += '</select></div></form><br />' - if error == 1: - out += "<i>User is not authorized to view such collection.</i><br />" - return out - elif error == 2: - out += "<i>There are no %s for this collection.</i><br />" % (comments and 'comments' or 'reviews') - return out - for cmt_tuple in comment_data: - bibrec_id = cmt_tuple[0] - content = format_record(bibrec_id, "hs") - last_comment_date = cmt_tuple[1] - total_users = cmt_tuple[2] - total_comments = cmt_tuple[3] - if comments: - comment_url = CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/comments' - str_comment = int(total_comments) > 1 and 'comments' or 'comment' - else: - comment_url = CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/reviews' - str_comment = int(total_comments) > 1 and 'reviews' or 'review' - out += """ - <li> %(content)s <br/> <span class="moreinfo"> <a class="moreinfo" href=%(comment_url)s> %(total_comments)s - %(str_comment)s</a> - (%(total_users)s %(user)s), latest on <i> %(last_comment_date)s </i></li> </span> <br/> - """ % {'content': content, - 'comment_url': comment_url , - 'total_comments': total_comments, - 'str_comment': str_comment, - 'total_users': total_users, - 'user': int(total_users) > 1 and 'users' or 'user', - 'last_comment_date': last_comment_date} - - out += """</ol>""" - return out - - def tmpl_admin_comments(self, ln, uid, comID, recID, comment_data, reviews, error, user_collections, collection): - """ - @param comment_data: same type of tuple as that - which is returned by webcomment.py/query_retrieve_comments_or_remarks i.e. - tuple of comment where comment is - tuple (nickname, - date_creation, - body, - id) if ranking disabled or - tuple (nickname, - date_creation, - body, - nb_votes_yes, - nb_votes_total, - star_score, - title, - id) - """ - _ = gettext_set_language(ln) - coll_form = """ - <script type='text/javascript'> - function collectionChange() - { - document.collection_form.submit(); - } - </script> - """ - coll_form += '<form method="get" name="collection_form" action="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&reviews=%s">' % (CFG_SITE_URL, ln, reviews) - coll_form += '<input type="hidden" name="ln" value=%s>' % ln - coll_form += '<input type="hidden" name="reviews" value=%s>' % reviews - coll_form += '<div> Filter by collection: <select name="collection" onchange="javascript:collectionChange();">' - for collection_name in user_collections: - if collection_name == collection: - coll_form += '<option "SELECTED" value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)} - else: - coll_form += '<option value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)} - coll_form += '</select></div></form><br />' - if error == 1: - coll_form += "<i>User is not authorized to view such collection.</i><br />" - return coll_form - elif error == 2: - coll_form += "<i>There are no %s for this collection.</i><br />" % (reviews and 'reviews' or 'comments') - return coll_form - - comments = [] - comments_info = [] - checkboxes = [] - users = [] - for (cmt_tuple, meta_data) in comment_data: - if reviews: - comments.append(self.tmpl_get_comment_with_ranking(None,#request object - ln, - cmt_tuple[0],#nickname - cmt_tuple[1],#userid - cmt_tuple[2],#date_creation - cmt_tuple[3],#body - cmt_tuple[9],#status - 0, - cmt_tuple[5],#nb_votes_total - cmt_tuple[4],#nb_votes_yes - cmt_tuple[6],#star_score - cmt_tuple[7],#title - admin_p=True)) - else: - comments.append(self.tmpl_get_comment_without_ranking(None,#request object - ln, - cmt_tuple[0],#nickname - cmt_tuple[1],#userid - cmt_tuple[2],#date_creation - cmt_tuple[3],#body - cmt_tuple[5],#status - 0, - None, #reply_link - None, #report_link - None, #undelete_link - None, #delete_links - None, #unreport_link - -1, # recid - cmt_tuple[4],# com_id - admin_p=True)) - users.append(self.tmpl_admin_user_info(ln, - meta_data[0], #nickname - meta_data[1], #uid - meta_data[2]))#email - if reviews: - status = cmt_tuple[9] - else: - status = cmt_tuple[5] - comments_info.append(self.tmpl_admin_review_info(ln, - reviews, - meta_data[5], # nb abuse reports - meta_data[3], # cmt_id - meta_data[4], # rec_id - status)) # status - checkboxes.append(self.tmpl_admin_select_comment_checkbox(meta_data[3])) - - form_link = "%s/admin/webcomment/webcommentadmin.py/del_com?ln=%s" % (CFG_SITE_URL, ln) - out = """ -<table class="admin_wvar" style="width:100%%;"> - <thead> - <tr class="adminheaderleft"> - <th>%(review_label)s</th> - <th>%(written_by_label)s</th> - <th>%(review_info_label)s</th> - <th>%(select_label)s</th> - </tr> - </thead> - <tbody>""" % {'review_label': reviews and _("Review") or _("Comment"), - 'written_by_label': _("Written by"), - 'review_info_label': _("General informations"), - 'select_label': _("Select")} - for i in range (0, len(comments)): - out += """ - <tr> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> - <td class="admintd" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> - </tr>""" % (comments[i], users[i], comments_info[i], checkboxes[i]) - out += """ - </tbody> -</table>""" - if reviews: - action_display = { - 'delete': _('Delete selected reviews'), - 'unreport': _('Suppress selected abuse report'), - 'undelete': _('Undelete selected reviews') - } - else: - action_display = { - 'undelete': _('Undelete selected comments'), - 'delete': _('Delete selected comments'), - 'unreport': _('Suppress selected abuse report') - } - - form = self.tmpl_create_multiple_actions_form(form_name="admin_comment", - form_action=form_link, - method="post", - action_display=action_display, - action_field_name='action', - button_label=_("OK"), - button_name="okbutton", - content=out) - if uid > 0: - header = '<br />' - if reviews: - header += _("Here are the reported reviews of user %(x_name)s", x_name=uid) - else: - header += _("Here are the reported comments of user %(x_name)s", x_name=uid) - header += '<br /><br />' - if comID > 0 and recID <= 0 and uid <= 0: - if reviews: - header = '<br />' +_("Here is review %(x_name)s", x_name=comID) + '<br /><br />' - else: - header = '<br />' +_("Here is comment %(x_name)s", x_name=comID) + '<br /><br />' - if uid > 0 and comID > 0 and recID <= 0: - if reviews: - header = '<br />' + _("Here is review %(x_cmtID)s written by user %(x_user)s") % {'x_cmtID': comID, 'x_user': uid} - else: - header = '<br />' + _("Here is comment %(x_cmtID)s written by user %(x_user)s") % {'x_cmtID': comID, 'x_user': uid} - header += '<br/ ><br />' - - if comID <= 0 and recID <= 0 and uid <= 0: - header = '<br />' - if reviews: - header += _("Here are all reported reviews sorted by the most reported") - else: - header += _("Here are all reported comments sorted by the most reported") - header += "<br /><br />" - elif recID > 0: - header = '<br />' - if reviews: - header += _("Here are all reviews for record %(x_num)i, sorted by the most reported", x_num=recID) - header += '<br /><a href="%s/admin/webcomment/webcommentadmin.py/delete?comid=&recid=%s&reviews=0">%s</a>' % (CFG_SITE_URL, recID, _("Show comments")) - else: - header += _("Here are all comments for record %(x_num)i, sorted by the most reported", x_num=recID) - header += '<br /><a href="%s/admin/webcomment/webcommentadmin.py/delete?comid=&recid=%s&reviews=1">%s</a>' % (CFG_SITE_URL, recID, _("Show reviews")) - - header += "<br /><br />" - return coll_form + header + form - - def tmpl_admin_del_com(self, del_res, ln=CFG_SITE_LANG): - """ - @param del_res: list of the following tuple (comment_id, was_successfully_deleted), - was_successfully_deleted is boolean (0=false, >0=true - """ - _ = gettext_set_language(ln) - table_rows = '' - for deltuple in del_res: - table_rows += """ -<tr> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> -</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>") - - out = """ -<table class="admin_wvar"> - <tr class="adminheaderleft"> - <td style="padding-right:10px;">%s</td> - <td>%s</td> - </tr>%s -<table>""" % (_("comment ID"), _("successfully deleted"), table_rows) - - return out - - def tmpl_admin_undel_com(self, del_res, ln=CFG_SITE_LANG): - """ - @param del_res: list of the following tuple (comment_id, was_successfully_undeleted), - was_successfully_undeleted is boolean (0=false, >0=true - """ - _ = gettext_set_language(ln) - table_rows = '' - for deltuple in del_res: - table_rows += """ -<tr> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> -</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>") - - out = """ -<table class="admin_wvar"> - <tr class="adminheaderleft"> - <td style="padding-right:10px;">%s</td> - <td>%s</td> - </tr>%s -<table>""" % (_("comment ID"), _("successfully undeleted"), table_rows) - - return out - - - - def tmpl_admin_suppress_abuse_report(self, del_res, ln=CFG_SITE_LANG): - """ - @param del_res: list of the following tuple (comment_id, was_successfully_deleted), - was_successfully_deleted is boolean (0=false, >0=true - """ - _ = gettext_set_language(ln) - table_rows = '' - for deltuple in del_res: - table_rows += """ -<tr> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> - <td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td> -</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>") - - out = """ -<table class="admin_wvar"> - <tr class="adminheaderleft"> - <td style ="padding-right: 10px;">%s</td> - <td>%s</td> - </tr>%s -<table>""" % (_("comment ID"), _("successfully suppressed abuse report"), table_rows) - - return out - - def tmpl_mini_review(self, recID, ln=CFG_SITE_LANG, action='SUBMIT', - avg_score=0, nb_comments_total=0): - """Display the mini version of reviews (only the grading part)""" - - _ = gettext_set_language(ln) - - url = '%s/%s/%s/reviews/add?ln=%s&action=%s' % (CFG_BASE_URL, CFG_SITE_RECORD, recID, ln, action) - - if avg_score > 0: - score = _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \ - {'x_nb_score': '<b>%.1f</b>' % avg_score, - 'x_nb_reviews': nb_comments_total} - else: - score = '(' +_("Not yet reviewed") + ')' - - if avg_score == 5: - s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', 'full' - elif avg_score >= 4.5: - s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', 'half' - elif avg_score >= 4: - s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', '' - elif avg_score >= 3.5: - s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'half', '' - elif avg_score >= 3: - s1, s2, s3, s4, s5 = 'full', 'full', 'full', '', '' - elif avg_score >= 2.5: - s1, s2, s3, s4, s5 = 'full', 'full', 'half', '', '' - elif avg_score >= 2: - s1, s2, s3, s4, s5 = 'full', 'full', '', '', '' - elif avg_score >= 1.5: - s1, s2, s3, s4, s5 = 'full', 'half', '', '', '' - elif avg_score == 1: - s1, s2, s3, s4, s5 = 'full', '', '', '', '' - else: - s1, s2, s3, s4, s5 = '', '', '', '', '' - - out = ''' -<small class="detailedRecordActions">%(rate)s:</small><br /><br /> -<div style="margin:auto;width:160px;"> -<span style="display:none;">Rate this document:</span> -<div class="star %(s1)s" ><a href="%(url)s&score=1">1</a> -<div class="star %(s2)s" ><a href="%(url)s&score=2">2</a> -<div class="star %(s3)s" ><a href="%(url)s&score=3">3</a> -<div class="star %(s4)s" ><a href="%(url)s&score=4">4</a> -<div class="star %(s5)s" ><a href="%(url)s&score=5">5</a></div></div></div></div></div> -<div style="clear:both"> </div> -</div> -<small>%(score)s</small> -''' % {'url': url, - 'score': score, - 'rate': _("Rate this document"), - 's1': s1, - 's2': s2, - 's3': s3, - 's4': s4, - 's5': s5 - } - return out - - def tmpl_email_new_comment_header(self, recID, title, reviews, - comID, report_numbers, - can_unsubscribe=True, - ln=CFG_SITE_LANG, uid=-1): - """ - Prints the email header used to notify subscribers that a new - comment/review was added. - - @param recid: the ID of the commented/reviewed record - @param title: the title of the commented/reviewed record - @param reviews: True if it is a review, else if a comment - @param comID: the comment ID - @param report_numbers: the report number(s) of the record - @param can_unsubscribe: True if user can unsubscribe from alert - @param ln: language - """ - # load the right message language - _ = gettext_set_language(ln) - - user_info = collect_user_info(uid) - - out = _("Hello:") + '\n\n' + \ - (reviews and _("The following review was sent to %(CFG_SITE_NAME)s by %(user_nickname)s:") or \ - _("The following comment was sent to %(CFG_SITE_NAME)s by %(user_nickname)s:")) % \ - {'CFG_SITE_NAME': CFG_SITE_NAME, - 'user_nickname': user_info['nickname']} - out += '\n(<%s>)' % (CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID)) - out += '\n\n\n' - return out - - def tmpl_email_new_comment_footer(self, recID, title, reviews, - comID, report_numbers, - can_unsubscribe=True, - ln=CFG_SITE_LANG): - """ - Prints the email footer used to notify subscribers that a new - comment/review was added. - - @param recid: the ID of the commented/reviewed record - @param title: the title of the commented/reviewed record - @param reviews: True if it is a review, else if a comment - @param comID: the comment ID - @param report_numbers: the report number(s) of the record - @param can_unsubscribe: True if user can unsubscribe from alert - @param ln: language - """ - # load the right message language - _ = gettext_set_language(ln) - - out = '\n\n-- \n' - out += _("This is an automatic message, please don't reply to it.") - out += '\n' - out += _("To post another comment, go to <%(x_url)s> instead.") % \ - {'x_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID) + \ - (reviews and '/reviews' or '/comments') + '/add'} - out += '\n' - if not reviews: - out += _("To specifically reply to this comment, go to <%(x_url)s>") % \ - {'x_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID) + \ - '/comments/add?action=REPLY&comid=' + str(comID)} - out += '\n' - if can_unsubscribe: - out += _("To unsubscribe from this discussion, go to <%(x_url)s>") % \ - {'x_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID) + \ - '/comments/unsubscribe'} - out += '\n' - out += _("For any question, please use <%(CFG_SITE_SUPPORT_EMAIL)s>") % \ - {'CFG_SITE_SUPPORT_EMAIL': CFG_SITE_SUPPORT_EMAIL} - - return out - - def tmpl_email_new_comment_admin(self, recID): - """ - Prints the record information used in the email to notify the - system administrator that a new comment has been posted. - - @param recID: the ID of the commented/reviewed record - """ - out = "" - - title = get_fieldvalues(recID, "245__a") - authors = ', '.join(get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")) - #res_author = "" - #res_rep_num = "" - #for author in authors: - # res_author = res_author + ' ' + author - dates = get_fieldvalues(recID, "260__c") - report_nums = get_fieldvalues(recID, "037__a") - report_nums += get_fieldvalues(recID, "088__a") - report_nums = ', '.join(report_nums) - #for rep_num in report_nums: - # res_rep_num = res_rep_num + ', ' + rep_num - out += " Title = %s \n" % (title and title[0] or "No Title") - out += " Authors = %s \n" % authors - if dates: - out += " Date = %s \n" % dates[0] - out += " Report number = %s" % report_nums - - return out - - def tmpl_page_do_not_leave_comment_page_js(self, ln): - """ - Code to ask user confirmation when leaving the page, so that the - comment is not lost if clicking by mistake on links. - - @param ln: the user language - """ - - # load the right message language - _ = gettext_set_language(ln) - - out = ''' - <script type="text/javascript" language="JavaScript">//<![CDATA[ - var initial_comment_value = document.forms.cmtForm.msg.value; - var user_must_confirm_before_leaving_page = true; - - window.onbeforeunload = confirmExit; - function confirmExit() { - var editor_type_field = document.getElementById('%(name)seditortype'); - if (editor_type_field && editor_type_field.value == 'ckeditor') { - var oEditor = CKEDITOR.instances.%(name)s; - if (user_must_confirm_before_leaving_page && oEditor.checkDirty()) { - /* Might give false positives, when editor pre-loaded - with content. But is better than the opposite */ - return "%(message)s"; - } - } else { - if (user_must_confirm_before_leaving_page && document.forms.cmtForm.msg.value != initial_comment_value){ - return "%(message)s"; - } - } - } - //]]></script> - ''' % {'message': _('Your comment will be lost.').replace('"', '\\"'), - 'name': 'msg'} - - return out - - def tmpl_your_comments(self, user_info, comments, page_number=1, selected_order_by_option="lcf", selected_display_number_option="all", selected_display_format_option="rc", nb_total_results=0, nb_total_pages=0, ln=CFG_SITE_LANG): - """ - Display all submitted comments by the user - - @param user_info: standard user info object. - @param comments: ordered list of tuples (id_bibrec, comid, date_creation, body, status, in_reply_to_id_cmtRECORDCOMMENT) - @param page_number: page on which the user is. - @type page_number: integer - @param selected_order_by_option: seleccted ordering option. Can be one of: - - ocf: Oldest comment first - - lcf: Latest comment first - - grof: Group by record, oldest commented first - - grlf: Group by record, latest commented first - @type selected_order_by_option: string - @param selected_display_number_option: number of results to show per page. Can be a string-digit or 'all'. - @type selected_display_number_option: string - @param selected_display_format_option: how to show records. Can be one of: - - rc: Records and comments - - ro: Records only - - co: Comments only - @type selected_display_format_option: string - @param nb_total_results: total number of items to display. - @type nb_total_results: integer - @param nb_total_pages: total number of pages. - @type nb_total_pages: integer - @ln: language - @type ln: string - """ - # load the right message language - _ = gettext_set_language(ln) - - from invenio.legacy.search_engine import record_exists - - your_comments_order_by_options = (('ocf', _("Oldest comment first")), - ('lcf', _("Latest comment first")), - ('grof', _("Group by record, oldest commented first")), - ('grlf', _("Group by record, latest commented first")), - ) - your_comments_display_format_options = (('rc', _("Records and comments")), - ('ro', _('Records only')), - ('co', _('Comments only')), - ) - your_comments_display_number_options = (('20', _("%(x_i)s items", x_i = 20)), - ('50', _("%(x_i)s items", x_i = 50)), - ('100', _("%(x_i)s items", x_i = 100)), - ('500',_("%(x_i)s items", x_i = 500)), - ('all', _('All items')), - ) - out = "" - out += _("Below is the list of the comments you have submitted so far.") + "<br/>" - - if CFG_CERN_SITE: - if nb_total_results == 0: - out = _('You have not yet submitted any comment in the document "discussion" tab.') + "<br/>" - user_roles = acc_get_user_roles_from_user_info(user_info) - if acc_get_role_id('ATLASDraftPublication') in user_roles: - out += _('You might find other comments here: ') - out += create_html_link(urlbase=CFG_SITE_URL + '/search', - urlargd={'ln': ln, - 'cc': 'ATLAS Publication Drafts Comments', - 'p': user_info['email'], - 'f': '859__f'}, - link_label='ATLAS Publication Drafts Comments') - elif acc_get_role_id('cmsphysicsmembers') in user_roles: - out += _('You might find other comments here: ') - out += create_html_link(urlbase=CFG_SITE_URL + '/search', - urlargd={'ln': ln, - 'cc': '', - 'p': user_info['email'], - 'f': '859__f'}, - link_label='CMS Publication Drafts Comments') - elif acc_get_role_id('LHCbDraftPublication') in user_roles: - out += _('You might find other comments here: ') - out += create_html_link(urlbase=CFG_SITE_URL + '/search', - urlargd={'ln': ln, - 'cc': '', - 'p': user_info['email'], - 'f': '859__f'}, - link_label='LHCb Publication Drafts Comments') - out += '<br/>' - if nb_total_results == 0: - return out - else: - if nb_total_results == 0: - return _("You have not yet submitted any comment. Browse documents from the search interface and take part to discussions!") - - - - # Show controls - format_selection = create_html_select(your_comments_display_format_options, - name="format", selected=selected_display_format_option, - attrs={'id': 'format', - 'onchange': 'this.form.submit();'}) - order_by_selection = create_html_select(your_comments_order_by_options, - name="order_by", selected=selected_order_by_option, - attrs={'id': 'order_by', - 'onchange': 'this.form.submit();'}) - nb_per_page_selection = create_html_select(your_comments_display_number_options, - name="per_page", selected=selected_display_number_option, - attrs={'id': 'per_page', - 'onchange': 'this.form.submit();'}) - - out += ''' - <form method="get" class="yourcommentsdisplayoptionsform"> - <fieldset id="yourcommentsdisplayoptions"> - <legend>%(display_option_label)s:</legend> - <label for="format">%(format_selection_label)s :</label> %(format_selection)s - <label for="order_by">%(order_selection_label)s :</label> %(order_by_selection)s - <label for="per_page">%(per_page_selection_label)s :</label> %(nb_per_page_selection)s - <noscript><input type="submit" value="%(refresh_label)s" class="formbutton"/></noscript> - </fieldset> - </form> - ''' % {'format_selection_label': _("Display"), - 'order_selection_label': _("Order by"), - 'per_page_selection_label': _("Per page"), - 'format_selection': format_selection, - 'order_by_selection': order_by_selection, - 'nb_per_page_selection': nb_per_page_selection, - 'display_option_label': _("Display options"), - 'refresh_label': _("Refresh"), - } - - # Show comments - last_id_bibrec = None - nb_record_groups = 0 - out += '<div id="yourcommentsmaincontent">' - for id_bibrec, comid, date_creation, body, status, in_reply_to_id_cmtRECORDCOMMENT in comments: - if last_id_bibrec != id_bibrec and selected_display_format_option in ('rc', 'ro'): - # We moved to another record. Show some info about - # current record. - if last_id_bibrec: - # Close previous group - out += "</div></div>" - nb_record_groups += 1 - # You might want to hide this information if user does - # not have access, though it would make sense that he - # can at least know on which page his comment appears.. - if record_exists(id_bibrec) == -1: - record_info_html = '<em>%s</em>' % _("The record has been deleted.") - else: - record_info_html = format_record(id_bibrec, of="HS") - out += '''<div class="yourcommentsrecordgroup" id="yourcomments-record-group-%(recid)s"> - <div class="yourcommentsrecordgroup%(recid)sheader">• ''' % {'recid': id_bibrec} + \ - record_info_html + '</div><div style="padding-left: 20px;">' - if selected_display_format_option != 'ro': - final_body = email_quoted_txt2html(body) - title = '<a name="C%s" id="C%s"></a>' % (comid, comid) - if status == "dm": - final_body = '<div class="webcomment_deleted_comment_message">%s</div>' % _("Comment deleted by the moderator") - elif status == "da": - final_body = ('<div class="webcomment_deleted_comment_message">%s<br /><br />' % _("You have deleted this comment: it is not visible by other users")) +\ - final_body + '</div>' - links = [] - if in_reply_to_id_cmtRECORDCOMMENT: - links.append(create_html_link(urlbase=CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + \ - str(id_bibrec) + '/comments/', - urlargd={'ln': ln}, - link_label=_('(in reply to a comment)'), - urlhash=str(in_reply_to_id_cmtRECORDCOMMENT))) - links.append(create_html_link(urlbase=CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + \ - str(id_bibrec) + '/comments/', - urlargd={'ln': ln}, - link_label=_('See comment on discussion page'), - urlhash='C' + str(comid))) - out += ''' - <div class="webcomment_comment_box"> - <div class="webcomment_comment_avatar"><img class="webcomment_comment_avatar_default" src="%(site_url)s/img/user-icon-1-24x24.gif" alt="avatar" /></div> - <div class="webcomment_comment_content"> - <div class="webcomment_comment_title"> - %(title)s - <div class="webcomment_comment_date">%(date)s</div> - <a class="webcomment_permalink" title="Permalink to this comment" href="#C%(comid)i">¶</a> - </div> - <div class="collapsible_content"> - <blockquote> - %(body)s - </blockquote> - <div class="webcomment_comment_options">%(links)s</div> - </div> - <div class="clearer"></div> - </div> - <div class="clearer"></div> - </div>''' % \ - {'title' : title, - 'body' : final_body, - 'links' : " ".join(links), - 'date' : date_creation, - 'site_url' : CFG_SITE_URL, - 'comid' : comid, - } - - last_id_bibrec = id_bibrec - - out += '</div>' # end 'yourcommentsmaincontent' - - # Show page navigation - page_links = '' - if selected_display_format_option == 'ro' and \ - selected_order_by_option in ('ocf', 'lcf'): - # We just have an approximation here (we count by - # comments, not record...) - page_links += (_("%(x_num)i comments found in total (not shown on this page)", x_num=nb_total_results)) + ' ' - else: - page_links += (_("%(x_num)i items found in total", x_num=nb_total_results)) + ' ' - if selected_display_number_option != 'all': - # Previous - if page_number != 1: - page_links += create_html_link(CFG_SITE_URL + '/yourcomments/', - {'page': page_number - 1, - 'order_by': selected_order_by_option, - 'per_page': selected_display_number_option, - 'format': selected_display_format_option, - 'ln': ln}, - _("Previous")) - # Page Numbers - for i in range(1, nb_total_pages + 1): - if i != page_number: - page_links += ' ' + \ - create_html_link(CFG_SITE_URL + '/yourcomments/', - {'page': i, - 'order_by': selected_order_by_option, - 'per_page': selected_display_number_option, - 'format': selected_display_format_option, - 'ln': ln}, - str(i)) + \ - ' ' - - elif nb_total_pages > 1: - page_links += ''' <b>%s</b> ''' % i - # Next - if page_number != nb_total_pages: - page_links += create_html_link(CFG_SITE_URL + '/yourcomments/', - {'page': page_number + 1, - 'order_by': selected_order_by_option, - 'per_page': selected_display_number_option, - 'format': selected_display_format_option, - 'ln': ln}, - _("Next")) - - out += '<br/><div id="yourcommentsnavigationlinks">' + page_links + '</div>' - - return out diff --git a/invenio/legacy/webcomment/web/__init__.py b/invenio/legacy/webcomment/web/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/invenio/legacy/webcomment/web/admin/__init__.py b/invenio/legacy/webcomment/web/admin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/invenio/legacy/webcomment/web/admin/webcommentadmin.py b/invenio/legacy/webcomment/web/admin/webcommentadmin.py deleted file mode 100644 index 9d81b5405..000000000 --- a/invenio/legacy/webcomment/web/admin/webcommentadmin.py +++ /dev/null @@ -1,376 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Invenio. -# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2015 CERN. -# -# Invenio is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# Invenio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Invenio; if not, write to the Free Software Foundation, Inc., -# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -"""Comments and reviews administrative interface.""" - -__revision__ = "$Id$" - -__lastupdated__ = """$Date$""" - -from invenio.legacy.webcomment.adminlib import * -from invenio.legacy.bibrank.adminlib import check_user -from invenio.legacy.webpage import page, create_error_box -from invenio.config import CFG_SITE_SECURE_URL,CFG_SITE_LANG,CFG_SITE_NAME -from invenio.legacy.webuser import getUid, page_not_authorized, collect_user_info -from invenio.utils.url import wash_url_argument, redirect_to_url -from invenio.base.i18n import wash_language, gettext_set_language -from invenio.modules.access.engine import acc_authorize_action - -from sqlalchemy.exc import SQLAlchemyError as Error - - -def index(req, ln=CFG_SITE_LANG): - """ - Menu of admin options - @param ln: language - """ - ln = wash_language(ln) - _ = gettext_set_language(ln) - navtrail_previous_links = getnavtrail() - navtrail_previous_links +=' > <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL - navtrail_previous_links += _("WebComment Admin") + '</a>' - - try: - uid = getUid(req) - except Error: - return page(title=_("Internal Error"), - body = create_error_box(req, verbose=0, ln=ln), - description="%s - Internal Error" % CFG_SITE_NAME, - keywords="%s, Internal Error" % CFG_SITE_NAME, - language=ln, - req=req) - - (auth_code, auth_msg) = check_user(req, 'cfgwebcomment') - if (auth_code != 'false'): - return page(title=_("WebComment Admin"), - body=perform_request_index(ln=ln), - uid=uid, - language=ln, - navtrail = navtrail_previous_links, - lastupdated=__lastupdated__, - req=req) - else: - return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links) - -def delete(req, ln=CFG_SITE_LANG, comid="", recid="", uid="", reviews=""): - """ - Delete a comment by giving its comment id - @param req: request object to obtain user information - @param ln: language - @param comid: comment id - @param recid: ID of the record containing the comment - @param uid: id of the user - @param reviews: boolean 1 if deleting a review, 0 if deleting a comment - """ - ln = wash_language(ln) - _ = gettext_set_language(ln) - navtrail_previous_links = getnavtrail() - navtrail_previous_links += ' > <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL - navtrail_previous_links += _("WebComment Admin") + '</a>' - - try: - uid = getUid(req) - except Error: - return page(title=_("Internal Error"), - body = create_error_box(req, verbose=0, ln=ln), - description="%s - Internal Error" % CFG_SITE_NAME, - keywords="%s, Internal Error" % CFG_SITE_NAME, - language=ln, - req=req) - - (auth_code, auth_msg) = check_user(req,'cfgwebcomment') - if (auth_code != 'false'): - body = perform_request_delete(ln=ln, - comID=comid, - recID=recid, - uid=uid, - reviews=reviews) - return page(title=(reviews=='1' and _("Delete/Undelete Reviews") or _("Delete/Undelete Comments")) + _(" or Suppress abuse reports"), - body=body, - uid=uid, - language=ln, - navtrail = navtrail_previous_links, - req = req, - lastupdated=__lastupdated__) - else: - return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links) - -def comments(req, ln=CFG_SITE_LANG, uid="", comid="", reviews=0, collection=""): - """ - View reported comments, filter by either user or a specific comment (only one given at a time) - @param req: request object to obtain user information - @param ln: language - @param uid: user id - @param comid: comment id - @param reviews: boolean enabled for reviews, disabled for comments - @param collection: filter results by collection - """ - ln = wash_language(ln) - _ = gettext_set_language(ln) - navtrail_previous_links = getnavtrail() - navtrail_previous_links += ' > <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL - navtrail_previous_links += _("WebComment Admin") + '</a>' - - try: - auid = getUid(req) - except Error: - return page(title=_("Internal Error"), - body = create_error_box(req, verbose=0, ln=ln), - description="%s - Internal Error" % CFG_SITE_NAME, - keywords="%s, Internal Error" % CFG_SITE_NAME, - language=ln, - req=req) - - (auth_code, auth_msg) = check_user(auid, 'cfgwebcomment') - if (auth_code != 'false'): - return page(title=(reviews=='0' and _("View all comments reported as abuse") or _("View all reviews reported as abuse")), - body=perform_request_comments(req, ln=ln, uid=uid, comID=comid, reviews=reviews, abuse=True, collection=collection), - uid=auid, - language=ln, - navtrail = navtrail_previous_links, - lastupdated=__lastupdated__, - req=req) - else: - return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links) - -def hot(req, ln=CFG_SITE_LANG, comments=1, top=10, collection=""): - """ - View most active comments/reviews - @param req: request object to obtain user information - @param ln: language - @param comments: boolean enabled for comments, disabled for reviews - @param top: number of results to be shown - @param collection: filter results by collection - """ - ln = wash_language(ln) - collection = wash_url_argument(collection, 'str') - _ = gettext_set_language(ln) - navtrail_previous_links = getnavtrail() - navtrail_previous_links += ' > <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL - navtrail_previous_links += _("WebComment Admin") + '</a>' - - user_info = collect_user_info(req) - (auth_code, auth_msg) = acc_authorize_action(user_info, 'cfgwebcomment') - if auth_code: - return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links) - return page(title=(comments=='0' and _("View most reviewed records") or - _("View most commented records")), - body=perform_request_hot(req, ln=ln, comments=comments, top=top, collection=collection), - uid=user_info['uid'], - language=ln, - navtrail = navtrail_previous_links, - lastupdated=__lastupdated__, - req=req) - -def latest(req, ln=CFG_SITE_LANG, comments=1, top=10, collection=""): - """ - View latest comments/reviews - @param req: request object to obtain user information - @param ln: language - @param comments: boolean enabled for comments, disabled for reviews - @param top: number of results to be shown - @param collection: filter results by collection - """ - ln = wash_language(ln) - _ = gettext_set_language(ln) - navtrail_previous_links = getnavtrail() - navtrail_previous_links += ' > <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL - navtrail_previous_links += _("WebComment Admin") + '</a>' - - try: - auid = getUid(req) - except Error: - return page(title=_("Internal Error"), - body = create_error_box(req, verbose=0, ln=ln), - description="%s - Internal Error" % CFG_SITE_NAME, - keywords="%s, Internal Error" % CFG_SITE_NAME, - language=ln, - req=req) - - (auth_code, auth_msg) = check_user(auid, 'cfgwebcomment') - if (auth_code != 'false'): - return page(title=(comments=='0' and _("View latest reviewed records") or - _("View latest commented records")), - body=perform_request_latest(req=req, ln=ln, comments=comments, top=top, collection=collection), - uid=auid, - language=ln, - navtrail = navtrail_previous_links, - lastupdated=__lastupdated__, - req=req) - else: - return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links) - - -def users(req, ln=CFG_SITE_LANG): - """ - View a list of all the users that have been reported, sorted by most reported - @param req: request object to obtain user information - @param ln: language - """ - ln = wash_language(ln) - _ = gettext_set_language(ln) - navtrail_previous_links = getnavtrail() - navtrail_previous_links += ' > <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL - navtrail_previous_links += _("WebComment Admin") + '</a>' - - try: - uid = getUid(req) - except Error: - return page(title=_("Internal Error"), - body = create_error_box(req, verbose=0, ln=ln), - description="%s - Internal Error" % CFG_SITE_NAME, - keywords="%s, Internal Error" % CFG_SITE_NAME, - language=ln, - req=req) - - (auth_code, auth_msg) = check_user(req,'cfgwebcomment') - if (auth_code != 'false'): - return page(title=_("View all reported users"), - body=perform_request_users(ln=ln), - uid=uid, - language=ln, - navtrail = navtrail_previous_links, - lastupdated=__lastupdated__, - req=req) - else: - - return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links) - -def del_com(req, ln=CFG_SITE_LANG, action="delete", **hidden): - """ - private function - Delete a comment - @param req: request object to obtain user information - @param ln: language - @param **hidden: ids of comments to delete sent as individual variables comidX=on, where X is id - """ - ln = wash_language(ln) - action = wash_url_argument(action, 'str') - _ = gettext_set_language(ln) - navtrail_previous_links = getnavtrail() - navtrail_previous_links += ' > <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL - navtrail_previous_links += _("WebComment Admin") + '</a>' - - try: - uid = getUid(req) - except Error: - return page(title=_("Internal Error"), - body = create_error_box(req, verbose=0, ln=ln), - description="%s - Internal Error" % CFG_SITE_NAME, - keywords="%s, Internal Error" % CFG_SITE_NAME, - language=ln, - req=req) - - (auth_code, auth_msg) = check_user(req,'cfgwebcomment') - if (auth_code != 'false'): - comIDs = [] - args = hidden.keys() - for var in args: - try: - comIDs.append(int(var.split('comid')[1])) - except: - pass - if action == 'delete': - body = perform_request_del_com(ln=ln, comIDs=comIDs) - title = _("Delete comments") - elif action == 'unreport': - body = suppress_abuse_report(ln=ln, comIDs=comIDs) - title = _("Suppress abuse reports") - elif action == 'undelete': - body = perform_request_undel_com(ln=ln, comIDs=comIDs) - title = _("Undelete comments") - else: - redirect_to_url(req, CFG_SITE_SECURE_URL + '/admin/webcomment/webcommentadmin.py') - return page(title=title, - body=body, - uid=uid, - language=ln, - navtrail = navtrail_previous_links, - lastupdated=__lastupdated__, - req=req) - else: - return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links) - -def undel_com(req, ln=CFG_SITE_LANG, id=""): - """ - Undelete a comment - @param req: request object to obtain user information - @param ln: language - @param id: comment id - """ - ln = wash_language(ln) - user_info = collect_user_info(req) - referer = user_info['referer'] - (auth_code, auth_msg) = check_user(req,'cfgwebcomment') - if (auth_code != 'false'): - perform_request_undel_single_com(ln=ln, id=id) - redirect_to_url(req, referer) - else: - return page_not_authorized(req=req, text=auth_msg) - -def del_single_com_mod(req, ln=CFG_SITE_LANG, id=""): - """ - Allow moderator to delete a single comment - @param req: request object to obtain user information - @param ln: language - @param id: comment id - """ - ln = wash_language(ln) - user_info = collect_user_info(req) - referer = user_info['referer'] - (auth_code, auth_msg) = check_user(req,'cfgwebcomment') - if (auth_code != 'false') or check_user_is_author(user_info['uid'], id): - perform_request_del_single_com_mod(ln=ln, id=id) - redirect_to_url(req, referer) - else: - return page_not_authorized(req=req, text=auth_msg) - -def del_single_com_auth(req, ln=CFG_SITE_LANG, id=""): - """ - Allow author to delete a single comment - @param req: request object to obtain user information - @param ln: language - @param id: comment id - """ - ln = wash_language(ln) - user_info = collect_user_info(req) - referer = user_info['referer'] - (auth_code, auth_msg) = check_user(req,'cfgwebcomment') - if (auth_code != 'false') or check_user_is_author(user_info['uid'], id): - perform_request_del_single_com_auth(ln=ln, id=id) - redirect_to_url(req, referer) - else: - return page_not_authorized(req=req, text=auth_msg) - -def unreport_com(req, ln=CFG_SITE_LANG, id=""): - """ - Unreport a comment - @param req: request object to obtain user information - @param ln: language - @param id: comment id - """ - ln = wash_language(ln) - user_info = collect_user_info(req) - referer = user_info['referer'] - (auth_code, auth_msg) = check_user(req,'cfgwebcomment') - if (auth_code != 'false'): - perform_request_unreport_single_com(ln=ln, id=id) - redirect_to_url(req, referer) - else: - return page_not_authorized(req=req, text=auth_msg) diff --git a/invenio/legacy/webcomment/webinterface.py b/invenio/legacy/webcomment/webinterface.py deleted file mode 100644 index d7a10eb8c..000000000 --- a/invenio/legacy/webcomment/webinterface.py +++ /dev/null @@ -1,941 +0,0 @@ -# -*- coding: utf-8 -*- -# Comments and reviews for records. - -# This file is part of Invenio. -# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2015 CERN. -# -# Invenio is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# Invenio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Invenio; if not, write to the Free Software Foundation, Inc., -# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -""" Comments and reviews for records: web interface """ - -__lastupdated__ = """$Date$""" - -__revision__ = """$Id$""" - -import cgi - -from six import iteritems - -from invenio.modules.comments.api 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, \ - toggle_comment_visibility, \ - check_comment_belongs_to_record, \ - is_comment_deleted, \ - perform_display_your_comments - -from invenio.config import \ - CFG_TMPSHAREDDIR, \ - CFG_SITE_LANG, \ - CFG_SITE_URL, \ - CFG_SITE_SECURE_URL, \ - CFG_SITE_NAME, \ - CFG_SITE_NAME_INTL, \ - CFG_WEBCOMMENT_ALLOW_COMMENTS,\ - CFG_WEBCOMMENT_ALLOW_REVIEWS, \ - CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS, \ - CFG_SITE_RECORD, \ - CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE, \ - CFG_WEBCOMMENT_MAX_ATTACHED_FILES, \ - CFG_ACCESS_CONTROL_LEVEL_SITE, \ - CFG_COMMENTSDIR -from invenio.legacy.webuser import getUid, page_not_authorized, isGuestUser, collect_user_info -from invenio.legacy.webpage import page, pageheaderonly, pagefooteronly -from invenio.legacy.search_engine import create_navtrail_links, \ - guess_primary_collection_of_a_record -from invenio.utils.url import redirect_to_url, \ - make_canonical_urlargd -from invenio.utils.html import get_mathjax_header -from invenio.ext.logging import register_exception -from invenio.base.i18n import gettext_set_language -from invenio.ext.legacy.handler import wash_urlargd, WebInterfaceDirectory -from invenio.legacy.websearch.adminlib import get_detailed_page_tabs, \ - get_detailed_page_tabs_counts -from invenio.modules.access.local_config import VIEWRESTRCOLL -from invenio.modules.access.mailcookie import \ - mail_cookie_create_authorize_action, \ - mail_cookie_create_common, \ - mail_cookie_check_common -from invenio.modules.access.errors import \ - InvenioWebAccessMailCookieDeletedError, \ - InvenioWebAccessMailCookieError -from invenio.modules.comments.config import \ - InvenioWebCommentWarning -import invenio.legacy.template -webstyle_templates = invenio.legacy.template.load('webstyle') -websearch_templates = invenio.legacy.template.load('websearch') -import os -from invenio.utils import apache -from invenio.legacy.bibdocfile.api import \ - stream_file, \ - decompose_file, \ - propose_next_docname -from invenio.modules.collections.models import Collection - - -class WebInterfaceCommentsPages(WebInterfaceDirectory): - - """Defines the set of /comments pages.""" - - _exports = ['', 'display', 'add', 'vote', 'report', 'index', 'attachments', - 'subscribe', 'unsubscribe', 'toggle'] - - 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': - cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)}) - target = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_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'): - 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 - - col_id = Collection.query.filter_by( - name=guess_primary_collection_of_a_record(self.recid)).value('id') - unordered_tabs = get_detailed_page_tabs(col_id, self.recid, - ln=argd['ln']) - ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in iteritems(unordered_tabs)] - ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1])) - link_ln = '' - if 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] - - tabs_counts = get_detailed_page_tabs_counts(self.recid) - citedbynum = tabs_counts['Citations'] - references = tabs_counts['References'] - discussions = tabs_counts['Discussions'] - - top = webstyle_templates.detailed_record_container_top(self.recid, - tabs, - argd['ln'], - citationnum=citedbynum, - referencenum=references, - discussionnum=discussions) - bottom = webstyle_templates.detailed_record_container_bottom(self.recid, - tabs, - argd['ln']) - - #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 = 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 - ) - - 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 += '<a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln']) - navtrail += cgi.escape(title) - navtrail += '</a>' - navtrail += ' > <a class="navtrail">%s</a>' % (self.discussion==1 and _("Reviews") or _("Comments")) - - mathjaxheader = '' - if CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS: - mathjaxheader = get_mathjax_header(req.is_https()) - jqueryheader = ''' - <script src="%(CFG_SITE_URL)s/vendors/jquery-multifile/jquery.MultiFile.pack.js" type="text/javascript"></script> - ''' % {'CFG_SITE_URL': CFG_SITE_URL} - - - return pageheaderonly(title=title, - navtrail=navtrail, - uid=uid, - verbose=1, - metaheaderadd = mathjaxheader + 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'], - navmenuid='search') - - # Return the same page wether we ask for /CFG_SITE_RECORD/123 or /CFG_SITE_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', 'ckeditor'. - @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, 0), - '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 = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_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) - - if argd['comid']: - # If replying to a comment, are we on a record that - # matches the original comment user is replying to? - if not check_comment_belongs_to_record(argd['comid'], self.recid): - return page_not_authorized(req, "../", \ - text = _("Specified comment does not belong to this record")) - - - # Is user trying to reply to a restricted comment? Make - # sure user has access to it. We will then inherit its - # restriction for the new comment - (auth_code, auth_msg) = check_user_can_view_comment(user_info, argd['comid']) - if auth_code: - return page_not_authorized(req, "../", \ - text = _("You do not have access to the specified comment")) - - # Is user trying to reply to a deleted comment? If so, we - # let submitted comment go (to not lose possibly submitted - # content, if comment is submitted while original is - # deleted), but we "reset" comid to make sure that for - # action 'REPLY' the original comment is not included in - # the reply - if is_comment_deleted(argd['comid']): - argd['comid'] = 0 - - 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'): - can_attach_files = True - - warning_msgs = [] # list of warning tuples (warning_text, warning_color) - 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_TMPSHAREDDIR, 'webcomment', str(uid)) - try: - assert(dir_to_open.startswith(CFG_TMPSHAREDDIR)) - 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 - 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 - try: - raise InvenioWebCommentWarning(_('The size of file \\"%(x_file)s\\" (%(x_size)s) is larger than maximum allowed file size (%(x_max)s). Select files again.', - x_file=cgi.escape(filename), x_size=str(file_size/1024) + 'KB', x_max=str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/1024) + 'KB')) - except InvenioWebCommentWarning as exc: - register_exception(stream='warning') - warning_msgs.append((exc.message, '')) - #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 += '<a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln']) - navtrail += cgi.escape(title) - navtrail += '</a>' - navtrail += '> <a class="navtrail" href="%s/%s/%s/%s/?ln=%s">%s</a>' % (CFG_SITE_URL, - CFG_SITE_RECORD, - 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: - return redirect_to_url(req, CFG_SITE_SECURE_URL + '/'+ CFG_SITE_RECORD +'/' + \ - str(self.recid) + (self.discussion==1 and \ - '/reviews' or '/comments')) - except InvenioWebAccessMailCookieError: - # 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 = 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 = ''' - <script src="%(CFG_SITE_URL)s/vendors/jquery-multifile/jquery.MultiFile.pack.js" type="text/javascript"></script> - ''' % {'CFG_SITE_URL': CFG_SITE_URL} - - return page(title=title, - body=body, - navtrail=navtrail, - uid=uid, - language=CFG_SITE_LANG, - verbose=1, - 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, - 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) - }) - _ = gettext_set_language(argd['ln']) - 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': - cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)}) - target = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_URL + user_info['uri']}, {}) - return redirect_to_url(req, target, norobot=True) - elif auth_code: - return page_not_authorized(req, "../", \ - text = auth_msg) - - # Check that comment belongs to this recid - if not check_comment_belongs_to_record(argd['comid'], self.recid): - return page_not_authorized(req, "../", \ - text = _("Specified comment does not belong to this record")) - - # Check that user can access the record - (auth_code, auth_msg) = check_user_can_view_comment(user_info, argd['comid']) - if auth_code: - return page_not_authorized(req, "../", \ - text = _("You do not have access to the specified comment")) - - # Check that comment is not currently deleted - if is_comment_deleted(argd['comid']): - return page_not_authorized(req, "../", \ - text = _("You cannot vote for a deleted comment"), - ln=argd['ln']) - - 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/%s/%s/%s?&ln=%s&voted=1" - referer %= (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, 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) - }) - _ = gettext_set_language(argd['ln']) - - 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 isGuestUser(uid): - cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)}) - target = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_URL + user_info['uri']}, {}) - return redirect_to_url(req, target, norobot=True) - elif auth_code: - return page_not_authorized(req, "../", \ - text = auth_msg) - - # Check that comment belongs to this recid - if not check_comment_belongs_to_record(argd['comid'], self.recid): - return page_not_authorized(req, "../", \ - text = _("Specified comment does not belong to this record")) - - # Check that user can access the record - (auth_code, auth_msg) = check_user_can_view_comment(user_info, argd['comid']) - if auth_code: - return page_not_authorized(req, "../", \ - text = _("You do not have access to the specified comment")) - - # Check that comment is not currently deleted - if is_comment_deleted(argd['comid']): - return page_not_authorized(req, "../", \ - text = _("You cannot report a deleted comment"), - ln=argd['ln']) - - 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/%s/%s/%s/display?ln=%s&voted=1" - referer %= (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, 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 = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_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/%s/%s/comments/display?subscribed=%s&ln=%s" % \ - (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, 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 = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_URL + user_info['uri']}, {}) - return redirect_to_url(req, target, norobot=True) - - success = unsubscribe_user_from_discussion(self.recid, uid) - display_url = "%s/%s/%s/comments/display?subscribed=%s&ln=%s" % \ - (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recid, str(-success), argd['ln']) - redirect_to_url(req, display_url) - - def toggle(self, req, form): - """ - Store the visibility of a comment for current user - """ - argd = wash_urlargd(form, {'comid': (int, -1), - 'referer': (str, None), - 'collapse': (int, 1)}) - - uid = getUid(req) - - if isGuestUser(uid): - # We do not store information for guests - return '' - - toggle_comment_visibility(uid, argd['comid'], argd['collapse'], self.recid) - if argd['referer']: - return redirect_to_url(req, CFG_SITE_SECURE_URL + \ - (not argd['referer'].startswith('/') and '/' or '') + \ - argd['referer'] + '#' + str(argd['comid'])) - -class WebInterfaceCommentsFiles(WebInterfaceDirectory): - """Handle <strike>upload and </strike> access to files for comments. - - <strike>The upload is currently only available through the Ckeditor.</strike> - """ - - #_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 - <strike>and putting attachments</strike>) Eg: - CFG_SITE_URL/CFG_SITE_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/CFG_SITE_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': - cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)}) - target = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_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': - cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)}) - target = CFG_SITE_SECURE_URL + '/youraccount/login' + \ - make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \ - CFG_SITE_SECURE_URL + user_info['uri']}, {}) - return redirect_to_url(req, target) - elif auth_code: - return page_not_authorized(req, "../", \ - text = auth_msg, - ln=argd['ln']) - - # Check that comment is not currently deleted - if is_comment_deleted(argd['comid']): - return page_not_authorized(req, "../", \ - text = _("You cannot access files of a deleted comment"), - 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(os.path.join(CFG_COMMENTSDIR, - str(self.recid), - str(argd['comid']), - argd['file'])) - - # Check that we are really accessing attachements - # directory, for the declared record. - if path.startswith(os.path.join(CFG_COMMENTSDIR, - 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']) - -class WebInterfaceYourCommentsPages(WebInterfaceDirectory): - """Defines the set of /yourcomments pages.""" - - _exports = ['', ] - - def index(self, req, form): - """Index page.""" - - argd = wash_urlargd(form, {'page': (int, 1), - 'format': (str, "rc"), - 'order_by': (str, "lcf"), - 'per_page': (str, "all"), - }) - # TODO: support also "reviews", by adding new option to show/hide them if needed - uid = getUid(req) - - # load the right language - _ = gettext_set_language(argd['ln']) - - # Is site ready to accept comments? - if not CFG_WEBCOMMENT_ALLOW_COMMENTS or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1: - return page_not_authorized(req, "%s/yourcomments" % \ - (CFG_SITE_SECURE_URL,), - text="Comments are currently disabled on this site", - navmenuid="yourcomments") - elif uid == -1 or isGuestUser(uid): - return redirect_to_url(req, "%s/youraccount/login%s" % ( - CFG_SITE_SECURE_URL, - make_canonical_urlargd({ - 'referer' : "%s/yourcomments%s" % ( - CFG_SITE_SECURE_URL, - make_canonical_urlargd(argd, {})), - "ln" : argd['ln']}, {}))) - - user_info = collect_user_info(req) - if not user_info['precached_sendcomments']: - # Maybe we should still authorize if user submitted - # comments in the past? - return page_not_authorized(req, "../", \ - text = _("You are not authorized to use comments.")) - - return page(title=_("Your Comments"), - body=perform_display_your_comments(user_info, - page_number=argd['page'], - selected_order_by_option=argd['order_by'], - selected_display_number_option=argd['per_page'], - selected_display_format_option=argd['format'], - ln=argd['ln']), - navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % { - 'sitesecureurl' : CFG_SITE_SECURE_URL, - 'ln': argd['ln'], - 'account' : _("Your Account"), - }, - description=_("%(x_name)s View your previously submitted comments", x_name=CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME)), - keywords=_("%(x_name)s, personalize", x_name=CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME)), - uid=uid, - language=argd['ln'], - req=req, - lastupdated=__lastupdated__, - navmenuid='youralerts', - secure_page_p=1) - - # Return the same page wether we ask for /CFG_SITE_RECORD/123 or /CFG_SITE_RECORD/123/ - __call__ = index diff --git a/invenio/legacy/websearch/adminlib.py b/invenio/legacy/websearch/adminlib.py index 00af19d5e..ec29dbdd1 100644 --- a/invenio/legacy/websearch/adminlib.py +++ b/invenio/legacy/websearch/adminlib.py @@ -1,3544 +1,3552 @@ # This file is part of Invenio. # Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, # 2014, 2015 CERN. # # Invenio is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # Invenio is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Invenio; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # pylint: disable=C0301 """Invenio WebSearch Administrator Interface.""" __revision__ = "$Id$" import cgi import random import time import sys from six import iteritems from invenio.utils.date import strftime if sys.hexversion < 0x2040000: # pylint: disable=W0622 from sets import Set as set # pylint: enable=W0622 from invenio.config import \ CFG_CACHEDIR, \ CFG_SITE_LANG, \ CFG_SITE_NAME, \ CFG_SITE_URL,\ CFG_WEBCOMMENT_ALLOW_COMMENTS, \ CFG_WEBSEARCH_SHOW_COMMENT_COUNT, \ CFG_WEBCOMMENT_ALLOW_REVIEWS, \ CFG_WEBSEARCH_SHOW_REVIEW_COUNT, \ CFG_BIBRANK_SHOW_CITATION_LINKS, \ CFG_INSPIRE_SITE, \ CFG_CERN_SITE from invenio.legacy.bibrank.adminlib import \ write_outcome, \ modify_translations, \ get_def_name, \ get_name, \ get_languages, \ addadminbox, \ tupletotable, \ createhiddenform from invenio.legacy.dbquery import \ run_sql, \ get_table_update_time from invenio.legacy.websearch_external_collections import \ external_collections_dictionary, \ external_collection_sort_engine_by_name, \ external_collection_get_state, \ external_collection_get_update_state_list, \ external_collection_apply_changes from invenio.legacy.websearch_external_collections.utils import \ get_collection_descendants from invenio.legacy.websearch_external_collections.config import CFG_EXTERNAL_COLLECTION_STATES_NAME #from invenio.modules.formatter.format_elements import bfe_references #from invenio.modules.formatter.engine import BibFormatObject from invenio.legacy.bibdocfile.api import BibRecDocs from invenio.base.i18n import gettext_set_language #from invenio.legacy.bibrank.citation_searcher import get_cited_by from invenio.modules.access.control import acc_get_action_id from invenio.modules.access.local_config import VIEWRESTRCOLL from invenio.ext.logging import register_exception from intbitset import intbitset from invenio.legacy.bibrank.citation_searcher import get_cited_by, get_cited_by_count from invenio.legacy.bibrecord import record_get_field_instances def getnavtrail(previous = ''): """Get the navtrail""" navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (CFG_SITE_URL,) navtrail = navtrail + previous return navtrail def fix_collection_scores(): """ Re-calculate and re-normalize de scores of the collection relationship. """ for id_dad in intbitset(run_sql("SELECT id_dad FROM collection_collection")): for index, id_son in enumerate(run_sql("SELECT id_son FROM collection_collection WHERE id_dad=%s ORDER BY score ASC", (id_dad, ))): run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s AND id_son=%s", (index, id_dad, id_son[0])) def perform_modifytranslations(colID, ln, sel_type='', trans=[], confirm=-1, callback='yes'): """Modify the translations of a collection sel_type - the nametype to modify trans - the translations in the same order as the languages from get_languages()""" output = '' subtitle = '' sitelangs = get_languages() if sel_type in ('r', 'v', 'l'): table = 'collectionbox' identifier_column = "id_collection" else: table = 'collection' identifier_column = None if type(trans) is str: trans = [trans] if confirm in ["2", 2] and colID: finresult = modify_translations(colID, sitelangs, sel_type, trans, table, identifier_column) col_dict = dict(get_def_name('', "collection")) if colID and int(colID) in col_dict: colID = int(colID) subtitle = """<a name="3">3. Modify translations for collection '%s'</a> <small>[<a href="%s/help/admin/websearch-admin-guide#3.3">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) if sel_type == '': sel_type = get_col_nametypes()[0][0] header = ['Language', 'Translation'] actions = [] types = get_col_nametypes() types.extend([('v', '"Focus on" box'), ('r', '"Narrow by" box'), ('l', '"Latest additions" box')]) if len(types) > 1: text = """ <span class="adminlabel">Name type</span> <select name="sel_type" class="admin_w200"> """ for (key, value) in types: text += """<option value="%s" %s>%s""" % (key, key == sel_type and 'selected="selected"' or '', value) trans_names = get_name(colID, ln, key, "collection") if trans_names and trans_names[0][0]: text += ": %s" % trans_names[0][0] text += "</option>" text += """</select>""" output += createhiddenform(action="modifytranslations#3", text=text, button="Select", colID=colID, ln=ln, confirm=0) if confirm in [-1, "-1", 0, "0"]: trans = [] for (key, value) in sitelangs: try: trans_names = get_name(colID, key, sel_type, table, identifier_column) trans.append(trans_names[0][0]) except StandardError as e: trans.append('') for nr in range(0, len(sitelangs)): actions.append(["%s" % (sitelangs[nr][1],)]) actions[-1].append('<input type="text" name="trans" size="30" value="%s"/>' % trans[nr]) text = tupletotable(header=header, tuple=actions) output += createhiddenform(action="modifytranslations#3", text=text, button="Modify", colID=colID, sel_type=sel_type, ln=ln, confirm=2) if sel_type and len(trans) and confirm in ["2", 2]: output += write_outcome(finresult) body = [output] if callback: return perform_editcollection(colID, ln, "perform_modifytranslations", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_modifyrankmethods(colID, ln, func='', rnkID='', confirm=0, callback='yes'): """Modify which rank methods is visible to the collection func - remove or add rank method rnkID - the id of the rank method.""" output = "" subtitle = "" col_dict = dict(get_def_name('', "collection")) rnk_dict = dict(get_def_name('', "rnkMETHOD")) if colID and int(colID) in col_dict: colID = int(colID) if func in ["0", 0] and confirm in ["1", 1]: finresult = attach_rnk_col(colID, rnkID) elif func in ["1", 1] and confirm in ["1", 1]: finresult = detach_rnk_col(colID, rnkID) subtitle = """<a name="9">9. Modify rank options for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.9">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) output = """ <dl> <dt>The rank methods enabled for the collection '%s' is:</dt> """ % col_dict[colID] rnkmethods = get_col_rnk(colID, ln) output += """<dd>""" if not rnkmethods: output += """No rank methods""" else: for id, name in rnkmethods: output += """%s, """ % name output += """</dd> </dl> """ rnk_list = get_def_name('', "rnkMETHOD") rnk_dict_in_col = dict(get_col_rnk(colID, ln)) rnk_list = filter(lambda x: x[0] not in rnk_dict_in_col, rnk_list) if rnk_list: text = """ <span class="adminlabel">Enable:</span> <select name="rnkID" class="admin_w200"> <option value="-1">- select rank method -</option> """ for (id, name) in rnk_list: text += """<option value="%s" %s>%s</option>""" % (id, (func in ["0", 0] and confirm in ["0", 0] and int(rnkID) == int(id)) and 'selected="selected"' or '' , name) text += """</select>""" output += createhiddenform(action="modifyrankmethods#9", text=text, button="Enable", colID=colID, ln=ln, func=0, confirm=1) if confirm in ["1", 1] and func in ["0", 0] and int(rnkID) != -1: output += write_outcome(finresult) elif confirm not in ["0", 0] and func in ["0", 0]: output += """<b><span class="info">Please select a rank method.</span></b>""" coll_list = get_col_rnk(colID, ln) if coll_list: text = """ <span class="adminlabel">Disable:</span> <select name="rnkID" class="admin_w200"> <option value="-1">- select rank method-</option> """ for (id, name) in coll_list: text += """<option value="%s" %s>%s</option>""" % (id, (func in ["1", 1] and confirm in ["0", 0] and int(rnkID) == int(id)) and 'selected="selected"' or '' , name) text += """</select>""" output += createhiddenform(action="modifyrankmethods#9", text=text, button="Disable", colID=colID, ln=ln, func=1, confirm=1) if confirm in ["1", 1] and func in ["1", 1] and int(rnkID) != -1: output += write_outcome(finresult) elif confirm not in ["0", 0] and func in ["1", 1]: output += """<b><span class="info">Please select a rank method.</span></b>""" body = [output] if callback: return perform_editcollection(colID, ln, "perform_modifyrankmethods", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_addcollectiontotree(colID, ln, add_dad='', add_son='', rtype='', mtype='', callback='yes', confirm=-1): """Form to add a collection to the tree. add_dad - the dad to add the collection to add_son - the collection to add rtype - add it as a regular or virtual mtype - add it to the regular or virtual tree.""" output = "" output2 = "" subtitle = """Attach collection to tree <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.2">?</a>]</small>""" % (CFG_SITE_URL) col_dict = dict(get_def_name('', "collection")) if confirm not in [-1, "-1"] and not (add_son and add_dad and rtype): output2 += """<b><span class="info">All fields must be filled.</span></b><br /><br /> """ elif add_son and add_dad and rtype: add_son = int(add_son) add_dad = int(add_dad) if confirm not in [-1, "-1"]: if add_son == add_dad: output2 += """<b><span class="info">Cannot add a collection as a pointer to itself.</span></b><br /><br /> """ elif check_col(add_dad, add_son): res = add_col_dad_son(add_dad, add_son, rtype) output2 += write_outcome(res) if res[0] == 1: output2 += """<b><span class="info"><br /> The collection will appear on your website after the next webcoll run. You can either run it manually or wait until bibsched does it for you.</span></b><br /><br /> """ else: output2 += """<b><span class="info">Cannot add the collection '%s' as a %s subcollection of '%s' since it will either create a loop, or the association already exists.</span></b><br /><br /> """ % (col_dict[add_son], (rtype=="r" and 'regular' or 'virtual'), col_dict[add_dad]) add_son = '' add_dad = '' rtype = '' tree = get_col_tree(colID) col_list = col_dict.items() col_list.sort(compare_on_val) output = show_coll_not_in_tree(colID, ln, col_dict) text = """ <span class="adminlabel">Attach collection:</span> <select name="add_son" class="admin_w200"> <option value="">- select collection -</option> """ for (id, name) in col_list: if id != colID: text += """<option value="%s" %s>%s</option>""" % (id, str(id)==str(add_son) and 'selected="selected"' or '', name) text += """ </select><br /> <span class="adminlabel">to parent collection:</span> <select name="add_dad" class="admin_w200"> <option value="">- select parent collection -</option> """ for (id, name) in col_list: text += """<option value="%s" %s>%s</option> """ % (id, str(id)==add_dad and 'selected="selected"' or '', name) text += """</select><br /> """ text += """ <span class="adminlabel">with relationship:</span> <select name="rtype" class="admin_w200"> <option value="">- select relationship -</option> <option value="r" %s>Regular (Narrow by...)</option> <option value="v" %s>Virtual (Focus on...)</option> </select> """ % ((rtype=="r" and 'selected="selected"' or ''), (rtype=="v" and 'selected="selected"' or '')) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollectiontotree" % CFG_SITE_URL, text=text, button="Add", colID=colID, ln=ln, confirm=1) output += output2 #output += perform_showtree(colID, ln) body = [output] if callback: return perform_index(colID, ln, mtype="perform_addcollectiontotree", content=addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_addcollection(colID, ln, colNAME='', dbquery='', callback="yes", confirm=-1): """form to add a new collection. colNAME - the name of the new collection dbquery - the dbquery of the new collection""" output = "" subtitle = """Create new collection <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.1">?</a>]</small>""" % (CFG_SITE_URL) text = """ <span class="adminlabel">Default name</span> <input class="admin_w200" type="text" name="colNAME" value="%s" /><br /> """ % colNAME output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollection" % CFG_SITE_URL, text=text, colID=colID, ln=ln, button="Add collection", confirm=1) if colNAME and confirm in ["1", 1]: res = add_col(colNAME, '') output += write_outcome(res) if res[0] == 1: output += perform_addcollectiontotree(colID=colID, ln=ln, add_son=res[1], callback='') elif confirm not in ["-1", -1]: output += """<b><span class="info">Please give the collection a name.</span></b>""" body = [output] if callback: return perform_index(colID, ln=ln, mtype="perform_addcollection", content=addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_modifydbquery(colID, ln, dbquery='', callback='yes', confirm=-1): """form to modify the dbquery of the collection. dbquery - the dbquery of the collection.""" subtitle = '' output = "" col_dict = dict(get_def_name('', "collection")) if colID and int(colID) in col_dict: colID = int(colID) subtitle = """<a name="1">1. Modify collection query for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.1">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) if confirm == -1: res = run_sql("SELECT dbquery FROM collection WHERE id=%s" % colID) dbquery = res[0][0] if not dbquery: dbquery = '' reg_sons = len(get_col_tree(colID, 'r')) vir_sons = len(get_col_tree(colID, 'v')) if reg_sons > 1: if dbquery: output += "Warning: This collection got subcollections, and should because of this not have a collection query, for further explanation, check the WebSearch Guide<br />" elif reg_sons <= 1: if not dbquery: output += "Warning: This collection does not have any subcollections, and should because of this have a collection query, for further explanation, check the WebSearch Guide<br />" text = """ <span class="adminlabel">Query</span> <input class="admin_w200" type="text" name="dbquery" value="%s" /><br /> """ % cgi.escape(dbquery, 1) output += createhiddenform(action="modifydbquery", text=text, button="Modify", colID=colID, ln=ln, confirm=1) if confirm in ["1", 1]: res = modify_dbquery(colID, dbquery) if res: if dbquery == "": text = """<b><span class="info">Query removed for this collection.</span></b>""" else: text = """<b><span class="info">Query set for this collection.</span></b>""" else: text = """<b><span class="info">Sorry, could not change query.</span></b>""" output += text body = [output] if callback: return perform_editcollection(colID, ln, "perform_modifydbquery", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_modifycollectiontree(colID, ln, move_up='', move_down='', move_from='', move_to='', delete='', rtype='', callback='yes', confirm=0): """to modify the collection tree: move a collection up and down, delete a collection, or change the father of the collection. colID - the main collection of the tree, the root move_up - move this collection up (is not the collection id, but the place in the tree) move_up - move this collection down (is not the collection id, but the place in the tree) move_from - move this collection from the current positon (is not the collection id, but the place in the tree) move_to - move the move_from collection and set this as it's father. (is not the collection id, but the place in the tree) delete - delete this collection from the tree (is not the collection id, but the place in the tree) rtype - the type of the collection in the tree, regular or virtual""" colID = int(colID) tree = get_col_tree(colID, rtype) col_dict = dict(get_def_name('', "collection")) subtitle = """Modify collection tree: %s <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.3">?</a>] <a href="%s/admin/websearch/websearchadmin.py/showtree?colID=%s&ln=%s">Printer friendly version</a></small>""" % (col_dict[colID], CFG_SITE_URL, CFG_SITE_URL, colID, ln) fin_output = "" output = "" try: if move_up: move_up = int(move_up) switch = find_last(tree, move_up) if switch and switch_col_treescore(tree[move_up], tree[switch]): output += """<b><span class="info">Moved the %s collection '%s' up and '%s' down.</span></b><br /><br /> """ % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]], col_dict[tree[switch][0]]) else: output += """<b><span class="info">Could not move the %s collection '%s' up and '%s' down.</span></b><br /><br /> """ % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]], col_dict[tree[switch][0]]) elif move_down: move_down = int(move_down) switch = find_next(tree, move_down) if switch and switch_col_treescore(tree[move_down], tree[switch]): output += """<b><span class="info">Moved the %s collection '%s' down and '%s' up.</span></b><br /><br /> """ % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_down][0]], col_dict[tree[switch][0]]) else: output += """<b><span class="info">Could not move the %s collection '%s' up and '%s' down.</span></b><br /><br /> """ % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]],col_dict[tree[switch][0]]) elif delete: delete = int(delete) if confirm in [0, "0"]: if col_dict[tree[delete][0]] != col_dict[tree[delete][3]]: text = """<b>Do you want to remove the %s collection '%s' and its subcollections in the %s collection '%s'.</b> """ % ((tree[delete][4]=="r" and 'regular' or 'virtual'), col_dict[tree[delete][0]], (rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]]) else: text = """<b>Do you want to remove all subcollections of the %s collection '%s'.</b> """ % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]]) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % CFG_SITE_URL, text=text, button="Confirm", colID=colID, delete=delete, rtype=rtype, ln=ln, confirm=1) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL, text="<b>To cancel</b>", button="Cancel", colID=colID, ln=ln) else: if remove_col_subcol(tree[delete][0], tree[delete][3], rtype): if col_dict[tree[delete][0]] != col_dict[tree[delete][3]]: output += """<b><span class="info">Removed the %s collection '%s' and its subcollections in subdirectory '%s'.</span></b><br /><br /> """ % ((tree[delete][4]=="r" and 'regular' or 'virtual'), col_dict[tree[delete][0]], col_dict[tree[delete][3]]) else: output += """<b><span class="info">Removed the subcollections of the %s collection '%s'.</span></b><br /><br /> """ % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]]) else: output += """<b><span class="info">Could not remove the collection from the tree.</span></b><br /><br /> """ delete = '' elif move_from and not move_to: move_from_rtype = move_from[0] move_from_id = int(move_from[1:len(move_from)]) text = """<b>Select collection to place the %s collection '%s' under.</b><br /><br /> """ % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_from_id][0]]) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL, text=text, button="Cancel", colID=colID, ln=ln) elif move_from and move_to: move_from_rtype = move_from[0] move_from_id = int(move_from[1:len(move_from)]) move_to_rtype = move_to[0] move_to_id = int(move_to[1:len(move_to)]) tree_from = get_col_tree(colID, move_from_rtype) tree_to = get_col_tree(colID, move_to_rtype) if confirm in [0, '0']: if move_from_id == move_to_id and move_from_rtype == move_to_rtype: output += """<b><span class="info">Cannot move to itself.</span></b><br /><br /> """ elif tree_from[move_from_id][3] == tree_to[move_to_id][0] and move_from_rtype==move_to_rtype: output += """<b><span class="info">The collection is already there.</span></b><br /><br /> """ elif check_col(tree_to[move_to_id][0], tree_from[move_from_id][0]) or (tree_to[move_to_id][0] == 1 and tree_from[move_from_id][3] == tree_to[move_to_id][0] and move_from_rtype != move_to_rtype): text = """<b>Move %s collection '%s' to the %s collection '%s'.</b> """ % ((tree_from[move_from_id][4]=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (tree_to[move_to_id][4]=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]]) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % CFG_SITE_URL, text=text, button="Confirm", colID=colID, move_from=move_from, move_to=move_to, ln=ln, rtype=rtype, confirm=1) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL, text="""<b>To cancel</b>""", button="Cancel", colID=colID, ln=ln) else: output += """<b><span class="info">Cannot move the collection '%s' and set it as a subcollection of '%s' since it will create a loop.</span></b><br /><br /> """ % (col_dict[tree_from[move_from_id][0]], col_dict[tree_to[move_to_id][0]]) else: if (move_to_id != 0 and move_col_tree(tree_from[move_from_id], tree_to[move_to_id])) or (move_to_id == 0 and move_col_tree(tree_from[move_from_id], tree_to[move_to_id], move_to_rtype)): output += """<b><span class="info">Moved %s collection '%s' to the %s collection '%s'.</span></b><br /><br /> """ % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (move_to_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]]) else: output += """<b><span class="info">Could not move %s collection '%s' to the %s collection '%s'.</span></b><br /><br /> """ % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (move_to_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]]) move_from = '' move_to = '' else: output += """ """ except StandardError as e: register_exception() return """<b><span class="info">An error occured.</span></b> """ output += """<table border ="0" width="100%"> <tr><td width="50%"> <b>Narrow by collection:</b> </td><td width="50%"> <b>Focus on...:</b> </td></tr><tr><td valign="top"> """ tree = get_col_tree(colID, 'r') output += create_colltree(tree, col_dict, colID, ln, move_from, move_to, 'r', "yes") output += """</td><td valign="top"> """ tree = get_col_tree(colID, 'v') output += create_colltree(tree, col_dict, colID, ln, move_from, move_to, 'v', "yes") output += """</td> </tr> </table> """ body = [output] if callback: return perform_index(colID, ln, mtype="perform_modifycollectiontree", content=addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_showtree(colID, ln): """create collection tree/hiarchy""" col_dict = dict(get_def_name('', "collection")) subtitle = "Collection tree: %s" % col_dict[int(colID)] output = """<table border ="0" width="100%"> <tr><td width="50%"> <b>Narrow by collection:</b> </td><td width="50%"> <b>Focus on...:</b> </td></tr><tr><td valign="top"> """ tree = get_col_tree(colID, 'r') output += create_colltree(tree, col_dict, colID, ln, '', '', 'r', '') output += """</td><td valign="top"> """ tree = get_col_tree(colID, 'v') output += create_colltree(tree, col_dict, colID, ln, '', '', 'v', '') output += """</td> </tr> </table> """ body = [output] return addadminbox(subtitle, body) def perform_addportalbox(colID, ln, title='', body='', callback='yes', confirm=-1): """form to add a new portalbox title - the title of the portalbox body - the body of the portalbox""" col_dict = dict(get_def_name('', "collection")) colID = int(colID) subtitle = """<a name="5.1"></a>Create new portalbox""" text = """ <span class="adminlabel">Title</span> <textarea cols="50" rows="1" class="admin_wvar" type="text" name="title">%s</textarea><br /> <span class="adminlabel">Body</span> <textarea cols="50" rows="10" class="admin_wvar" type="text" name="body">%s</textarea><br /> """ % (cgi.escape(title), cgi.escape(body)) output = createhiddenform(action="addportalbox#5.1", text=text, button="Add", colID=colID, ln=ln, confirm=1) if body and confirm in [1, "1"]: res = add_pbx(title, body) output += write_outcome(res) if res[1] == 1: output += """<b><span class="info"><a href="addexistingportalbox?colID=%s&ln=%s&pbxID=%s#5">Add portalbox to collection</a></span></b>""" % (colID, ln, res[1]) elif confirm not in [-1, "-1"]: output += """<b><span class="info">Body field must be filled.</span></b> """ body = [output] return perform_showportalboxes(colID, ln, content=addadminbox(subtitle, body)) def perform_addexistingportalbox(colID, ln, pbxID=-1, score=0, position='', sel_ln='', callback='yes', confirm=-1): """form to add an existing portalbox to a collection. colID - the collection to add the portalbox to pbxID - the portalbox to add score - the importance of the portalbox. position - the position of the portalbox on the page sel_ln - the language of the portalbox""" subtitle = """<a name="5.2"></a>Add existing portalbox to collection""" output = "" colID = int(colID) res = get_pbx() pos = get_pbx_pos() lang = dict(get_languages()) col_dict = dict(get_def_name('', "collection")) pbx_dict = dict(map(lambda x: (x[0], x[1]), res)) col_pbx = get_col_pbx(colID) col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx)) if len(res) > 0: text = """ <span class="adminlabel">Portalbox</span> <select name="pbxID" class="admin_w200"> <option value="-1">- Select portalbox -</option> """ for (id, t_title, t_body) in res: text += """<option value="%s" %s>%s - %s...</option>\n""" % \ (id, id == int(pbxID) and 'selected="selected"' or '', t_title[:40], cgi.escape(t_body[0:40 - min(40, len(t_title))])) text += """</select><br /> <span class="adminlabel">Language</span> <select name="sel_ln" class="admin_w200"> <option value="">- Select language -</option> """ listlang = lang.items() listlang.sort() for (key, name) in listlang: text += """<option value="%s" %s>%s</option> """ % (key, key == sel_ln and 'selected="selected"' or '', name) text += """</select><br /> <span class="adminlabel">Position</span> <select name="position" class="admin_w200"> <option value="">- Select position -</option> """ listpos = pos.items() listpos.sort() for (key, name) in listpos: text += """<option value="%s" %s>%s</option>""" % (key, key==position and 'selected="selected"' or '', name) text += "</select>" output += createhiddenform(action="addexistingportalbox#5.2", text=text, button="Add", colID=colID, ln=ln, confirm=1) else: output = """No existing portalboxes to add, please create a new one. """ if pbxID > -1 and position and sel_ln and confirm in [1, "1"]: pbxID = int(pbxID) res = add_col_pbx(colID, pbxID, sel_ln, position, '') output += write_outcome(res) elif pbxID > -1 and confirm not in [-1, "-1"]: output += """<b><span class="info">All fields must be filled.</span></b> """ body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_showportalboxes(colID, ln, content=output) def perform_deleteportalbox(colID, ln, pbxID=-1, callback='yes', confirm=-1): """form to delete a portalbox which is not in use. colID - the current collection. pbxID - the id of the portalbox""" subtitle = """<a name="5.3"></a>Delete an unused portalbox""" output = "" colID = int(colID) if pbxID not in [-1, "-1"] and confirm in [1, "1"]: ares = get_pbx() pbx_dict = dict(map(lambda x: (x[0], x[1]), ares)) if int(pbxID) in pbx_dict: pname = pbx_dict[int(pbxID)] ares = delete_pbx(int(pbxID)) else: return """<b><span class="info">This portalbox does not exist</span></b>""" res = get_pbx() col_dict = dict(get_def_name('', "collection")) pbx_dict = dict(map(lambda x: (x[0], x[1]), res)) col_pbx = get_col_pbx() col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx)) if len(res) > 0: text = """ <span class="adminlabel">Portalbox</span> <select name="pbxID" class="admin_w200"> """ text += """<option value="-1">- Select portalbox -""" for (id, t_title, t_body) in res: if id not in col_pbx: text += """<option value="%s" %s>%s - %s...""" % (id, id == int(pbxID) and 'selected="selected"' or '', t_title, cgi.escape(t_body[0:10])) text += "</option>" text += """</select><br />""" output += createhiddenform(action="deleteportalbox#5.3", text=text, button="Delete", colID=colID, ln=ln, confirm=1) if pbxID not in [-1, "-1"]: pbxID = int(pbxID) if confirm in [1, "1"]: output += write_outcome(ares) elif confirm not in [-1, "-1"]: output += """<b><span class="info">Choose a portalbox to delete.</span></b> """ body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_showportalboxes(colID, ln, content=output) def perform_modifyportalbox(colID, ln, pbxID=-1, score='', position='', sel_ln='', title='', body='', callback='yes', confirm=-1): """form to modify a portalbox in a collection, or change the portalbox itself. colID - the id of the collection. pbxID - the portalbox to change score - the score of the portalbox connected to colID which should be changed. position - the position of the portalbox in collection colID to change.""" subtitle = "" output = "" colID = int(colID) res = get_pbx() pos = get_pbx_pos() lang = dict(get_languages()) col_dict = dict(get_def_name('', "collection")) pbx_dict = dict(map(lambda x: (x[0], x[1]), res)) col_pbx = get_col_pbx(colID) col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx)) if pbxID not in [-1, "-1"]: pbxID = int(pbxID) subtitle = """<a name="5.4"></a>Modify portalbox '%s' for this collection""" % pbx_dict[pbxID] col_pbx = get_col_pbx(colID) if not (score and position) and not (body and title): for (id_pbx, id_collection, tln, score, position, title, body) in col_pbx: if id_pbx == pbxID: break output += """Collection (presentation) specific values (Changes implies only to this collection.)<br />""" text = """ <span class="adminlabel">Position</span> <select name="position" class="admin_w200"> """ listpos = pos.items() listpos.sort() for (key, name) in listpos: text += """<option value="%s" %s>%s""" % (key, key==position and 'selected="selected"' or '', name) text += "</option>" text += """</select><br />""" output += createhiddenform(action="modifyportalbox#5.4", text=text, button="Modify", colID=colID, pbxID=pbxID, score=score, title=title, body=cgi.escape(body, 1), sel_ln=sel_ln, ln=ln, confirm=3) if pbxID > -1 and score and position and confirm in [3, "3"]: pbxID = int(pbxID) res = modify_pbx(colID, pbxID, sel_ln, score, position, '', '') res2 = get_pbx() pbx_dict = dict(map(lambda x: (x[0], x[1]), res2)) output += write_outcome(res) output += """<br />Portalbox (content) specific values (any changes appears everywhere the portalbox is used.)""" text = """ <span class="adminlabel">Title</span> <textarea cols="50" rows="1" class="admin_wvar" type="text" name="title">%s</textarea><br /> """ % cgi.escape(title) text += """ <span class="adminlabel">Body</span> <textarea cols="50" rows="10" class="admin_wvar" type="text" name="body">%s</textarea><br /> """ % cgi.escape(body) output += createhiddenform(action="modifyportalbox#5.4", text=text, button="Modify", colID=colID, pbxID=pbxID, sel_ln=sel_ln, score=score, position=position, ln=ln, confirm=4) if pbxID > -1 and confirm in [4, "4"]: pbxID = int(pbxID) res = modify_pbx(colID, pbxID, sel_ln, '', '', title, body) output += write_outcome(res) else: output = """No portalbox to modify.""" body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_showportalboxes(colID, ln, content=output) def perform_switchpbxscore(colID, id_1, id_2, sel_ln, ln): """Switch the score of id_1 and id_2 in collection_portalbox. colID - the current collection id_1/id_2 - the id's to change the score for. sel_ln - the language of the portalbox""" output = "" res = get_pbx() pbx_dict = dict(map(lambda x: (x[0], x[1]), res)) res = switch_pbx_score(colID, id_1, id_2, sel_ln) output += write_outcome(res) return perform_showportalboxes(colID, ln, content=output) def perform_showportalboxes(colID, ln, callback='yes', content='', confirm=-1): """show the portalboxes of this collection. colID - the portalboxes to show the collection for.""" colID = int(colID) col_dict = dict(get_def_name('', "collection")) subtitle = """<a name="5">5. Modify portalboxes for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.5">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) output = "" pos = get_pbx_pos() output = """<dl> <dt>Portalbox actions (not related to this collection)</dt> <dd><a href="addportalbox?colID=%s&ln=%s#5.1">Create new portalbox</a></dd> <dd><a href="deleteportalbox?colID=%s&ln=%s#5.3">Delete an unused portalbox</a></dd> <dt>Collection specific actions</dt> <dd><a href="addexistingportalbox?colID=%s&ln=%s#5.2">Add existing portalbox to collection</a></dd> </dl> """ % (colID, ln, colID, ln, colID, ln) header = ['Position', 'Language', '', 'Title', 'Actions'] actions = [] sitelangs = get_languages() lang = dict(sitelangs) pos_list = pos.items() pos_list.sort() if len(get_col_pbx(colID)) > 0: for (key, value) in sitelangs: for (pos_key, pos_value) in pos_list: res = get_col_pbx(colID, key, pos_key) i = 0 for (pbxID, colID_pbx, tln, score, position, title, body) in res: move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>""" if i != 0: move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&ln=%s&id_1=%s&id_2=%s&sel_ln=%s&rand=%s#5"><img border="0" src="%s/img/smallup.gif" title="Move portalbox up" alt="up" /></a>""" % (CFG_SITE_URL, colID, ln, pbxID, res[i - 1][0], tln, random.randint(0, 1000), CFG_SITE_URL) else: move += " " move += "</td><td>" i += 1 if i != len(res): move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&ln=%s&id_1=%s&id_2=%s&sel_ln=%s&rand=%s#5"><img border="0" src="%s/img/smalldown.gif" title="Move portalbox down" alt="down" /></a>""" % (CFG_SITE_URL, colID, ln, pbxID, res[i][0], tln, random.randint(0, 1000), CFG_SITE_URL) move += """</td></tr></table>""" actions.append(["%s" % (i==1 and pos[position] or ''), "%s" % (i==1 and lang[tln] or ''), move, "%s" % title]) for col in [(('Modify', 'modifyportalbox'), ('Remove', 'removeportalbox'),)]: actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&pbxID=%s&sel_ln=%s#5.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, pbxID, tln, col[0][0])) for (str, function) in col[1:]: actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&pbxID=%s&sel_ln=%s#5.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, pbxID, tln, str) output += tupletotable(header=header, tuple=actions) else: output += """No portalboxes exists for this collection""" output += content body = [output] if callback: return perform_editcollection(colID, ln, "perform_showportalboxes", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_removeportalbox(colID, ln, pbxID='', sel_ln='', callback='yes', confirm=0): """form to remove a portalbox from a collection. colID - the current collection, remove the portalbox from this collection. sel_ln - remove the portalbox with this language pbxID - remove the portalbox with this id""" subtitle = """<a name="5.5"></a>Remove portalbox""" output = "" col_dict = dict(get_def_name('', "collection")) res = get_pbx() pbx_dict = dict(map(lambda x: (x[0], x[1]), res)) if colID and pbxID and sel_ln: colID = int(colID) pbxID = int(pbxID) if confirm in ["0", 0]: text = """Do you want to remove the portalbox '%s' from the collection '%s'.""" % (pbx_dict[pbxID], col_dict[colID]) output += createhiddenform(action="removeportalbox#5.5", text=text, button="Confirm", colID=colID, pbxID=pbxID, sel_ln=sel_ln, confirm=1) elif confirm in ["1", 1]: res = remove_pbx(colID, pbxID, sel_ln) output += write_outcome(res) body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_showportalboxes(colID, ln, content=output) def perform_switchfmtscore(colID, type, id_1, id_2, ln): """Switch the score of id_1 and id_2 in the table type. colID - the current collection id_1/id_2 - the id's to change the score for. type - like "format" """ fmt_dict = dict(get_def_name('', "format")) res = switch_score(colID, id_1, id_2, type) output = write_outcome(res) return perform_showoutputformats(colID, ln, content=output) def perform_switchfldscore(colID, id_1, id_2, fmeth, ln): """Switch the score of id_1 and id_2 in collection_field_fieldvalue. colID - the current collection id_1/id_2 - the id's to change the score for.""" fld_dict = dict(get_def_name('', "field")) res = switch_fld_score(colID, id_1, id_2) output = write_outcome(res) if fmeth == "soo": return perform_showsortoptions(colID, ln, content=output) elif fmeth == "sew": return perform_showsearchfields(colID, ln, content=output) elif fmeth == "seo": return perform_showsearchoptions(colID, ln, content=output) def perform_switchfldvaluescore(colID, id_1, id_fldvalue_1, id_fldvalue_2, ln): """Switch the score of id_1 and id_2 in collection_field_fieldvalue. colID - the current collection id_1/id_2 - the id's to change the score for.""" name_1 = run_sql("SELECT name from fieldvalue where id=%s", (id_fldvalue_1, ))[0][0] name_2 = run_sql("SELECT name from fieldvalue where id=%s", (id_fldvalue_2, ))[0][0] res = switch_fld_value_score(colID, id_1, id_fldvalue_1, id_fldvalue_2) output = write_outcome(res) return perform_modifyfield(colID, fldID=id_1, ln=ln, content=output) def perform_addnewfieldvalue(colID, fldID, ln, name='', value='', callback="yes", confirm=-1): """form to add a new fieldvalue. name - the name of the new fieldvalue value - the value of the new fieldvalue """ output = "" subtitle = """<a name="7.4"></a>Add new value""" text = """ <span class="adminlabel">Display name</span> <input class="admin_w200" type="text" name="name" value="%s" /><br /> <span class="adminlabel">Search value</span> <input class="admin_w200" type="text" name="value" value="%s" /><br /> """ % (name, value) output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addnewfieldvalue" % CFG_SITE_URL, text=text, colID=colID, fldID=fldID, ln=ln, button="Add", confirm=1) if name and value and confirm in ["1", 1]: res = add_fldv(name, value) output += write_outcome(res) if res[0] == 1: res = add_col_fld(colID, fldID, 'seo', res[1]) if res[0] == 0: output += "<br />" + write_outcome(res) elif confirm not in ["-1", -1]: output += """<b><span class="info">Please fill in name and value.</span></b> """ body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output) def perform_modifyfieldvalue(colID, fldID, fldvID, ln, name='', value='', callback="yes", confirm=-1): """form to modify a fieldvalue. name - the name of the fieldvalue value - the value of the fieldvalue """ if confirm in [-1, "-1"]: res = get_fld_value(fldvID) (id, name, value) = res[0] output = "" subtitle = """<a name="7.4"></a>Modify existing value""" output = """<dl> <dt><b><span class="info">Warning: Modifications done below will also inflict on all places the modified data is used.</span></b></dt> </dl>""" text = """ <span class="adminlabel">Display name</span> <input class="admin_w200" type="text" name="name" value="%s" /><br /> <span class="adminlabel">Search value</span> <input class="admin_w200" type="text" name="value" value="%s" /><br /> """ % (name, value) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % CFG_SITE_URL, text=text, colID=colID, fldID=fldID, fldvID=fldvID, ln=ln, button="Update", confirm=1) output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % CFG_SITE_URL, text="Delete value and all associations", colID=colID, fldID=fldID, fldvID=fldvID, ln=ln, button="Delete", confirm=2) if name and value and confirm in ["1", 1]: res = update_fldv(fldvID, name, value) output += write_outcome(res) #if res: # output += """<b><span class="info">Operation successfully completed.</span></b>""" #else: # output += """<b><span class="info">Operation failed.</span></b>""" elif confirm in ["2", 2]: res = delete_fldv(fldvID) output += write_outcome(res) elif confirm not in ["-1", -1]: output += """<b><span class="info">Please fill in name and value.</span></b>""" body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output) def perform_removefield(colID, ln, fldID='', fldvID='', fmeth='', callback='yes', confirm=0): """form to remove a field from a collection. colID - the current collection, remove the field from this collection. sel_ln - remove the field with this language fldID - remove the field with this id""" if fmeth == "soo": field = "sort option" elif fmeth == "sew": field = "search field" elif fmeth == "seo": field = "search option" else: field = "field" subtitle = """<a name="6.4"><a name="7.4"><a name="8.4"></a>Remove %s""" % field output = "" col_dict = dict(get_def_name('', "collection")) fld_dict = dict(get_def_name('', "field")) res = get_fld_value() fldv_dict = dict(map(lambda x: (x[0], x[1]), res)) if colID and fldID: colID = int(colID) fldID = int(fldID) if fldvID and fldvID != "None": fldvID = int(fldvID) if confirm in ["0", 0]: text = """Do you want to remove the %s '%s' %s from the collection '%s'.""" % (field, fld_dict[fldID], (fldvID not in["", "None"] and "with value '%s'" % fldv_dict[fldvID] or ''), col_dict[colID]) output += createhiddenform(action="removefield#6.5", text=text, button="Confirm", colID=colID, fldID=fldID, fldvID=fldvID, fmeth=fmeth, confirm=1) elif confirm in ["1", 1]: res = remove_fld(colID, fldID, fldvID) output += write_outcome(res) body = [output] output = "<br />" + addadminbox(subtitle, body) if fmeth == "soo": return perform_showsortoptions(colID, ln, content=output) elif fmeth == "sew": return perform_showsearchfields(colID, ln, content=output) elif fmeth == "seo": return perform_showsearchoptions(colID, ln, content=output) def perform_removefieldvalue(colID, ln, fldID='', fldvID='', fmeth='', callback='yes', confirm=0): """form to remove a field from a collection. colID - the current collection, remove the field from this collection. sel_ln - remove the field with this language fldID - remove the field with this id""" subtitle = """<a name="7.4"></a>Remove value""" output = "" col_dict = dict(get_def_name('', "collection")) fld_dict = dict(get_def_name('', "field")) res = get_fld_value() fldv_dict = dict(map(lambda x: (x[0], x[1]), res)) if colID and fldID: colID = int(colID) fldID = int(fldID) if fldvID and fldvID != "None": fldvID = int(fldvID) if confirm in ["0", 0]: text = """Do you want to remove the value '%s' from the search option '%s'.""" % (fldv_dict[fldvID], fld_dict[fldID]) output += createhiddenform(action="removefieldvalue#7.4", text=text, button="Confirm", colID=colID, fldID=fldID, fldvID=fldvID, fmeth=fmeth, confirm=1) elif confirm in ["1", 1]: res = remove_fld(colID, fldID, fldvID) output += write_outcome(res) body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output) def perform_rearrangefieldvalue(colID, fldID, ln, callback='yes', confirm=-1): """rearrang the fieldvalues alphabetically colID - the collection fldID - the field to rearrange the fieldvalue for """ subtitle = "Order values alphabetically" output = "" col_fldv = get_col_fld(colID, 'seo', fldID) col_fldv = dict(map(lambda x: (x[1], x[0]), col_fldv)) fldv_names = get_fld_value() fldv_names = map(lambda x: (x[0], x[1]), fldv_names) if None not in col_fldv: vscore = len(col_fldv) for (fldvID, name) in fldv_names: if fldvID in col_fldv: run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (vscore, colID, fldID, fldvID)) vscore -= 1 output += write_outcome((1, "")) else: output += write_outcome((0, (0, "No values to order"))) body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_modifyfield(colID, fldID, ln, content=output) def perform_rearrangefield(colID, ln, fmeth, callback='yes', confirm=-1): """rearrang the fields alphabetically colID - the collection """ subtitle = "Order fields alphabetically" output = "" col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, fmeth))) fld_names = get_def_name('', "field") if len(col_fld) > 0: score = len(col_fld) for (fldID, name) in fld_names: if fldID in col_fld: run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s", (score, colID, fldID)) score -= 1 output += write_outcome((1, "")) else: output += write_outcome((0, (0, "No fields to order"))) body = [output] output = "<br />" + addadminbox(subtitle, body) if fmeth == "soo": return perform_showsortoptions(colID, ln, content=output) elif fmeth == "sew": return perform_showsearchfields(colID, ln, content=output) elif fmeth == "seo": return perform_showsearchoptions(colID, ln, content=output) def perform_addexistingfieldvalue(colID, fldID, fldvID=-1, ln=CFG_SITE_LANG, callback='yes', confirm=-1): """form to add an existing fieldvalue to a field. colID - the collection fldID - the field to add the fieldvalue to fldvID - the fieldvalue to add""" subtitle = """</a><a name="7.4"></a>Add existing value to search option""" output = "" if fldvID not in [-1, "-1"] and confirm in [1, "1"]: fldvID = int(fldvID) ares = add_col_fld(colID, fldID, 'seo', fldvID) colID = int(colID) fldID = int(fldID) lang = dict(get_languages()) res = get_def_name('', "field") col_dict = dict(get_def_name('', "collection")) fld_dict = dict(res) col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, 'seo'))) fld_value = get_fld_value() fldv_dict = dict(map(lambda x: (x[0], x[1]), fld_value)) text = """ <span class="adminlabel">Value</span> <select name="fldvID" class="admin_w200"> <option value="-1">- Select value -</option> """ res = run_sql("SELECT id,name,value FROM fieldvalue ORDER BY name") for (id, name, value) in res: text += """<option value="%s" %s>%s - %s</option> """ % (id, id == int(fldvID) and 'selected="selected"' or '', name, value) text += """</select><br />""" output += createhiddenform(action="addexistingfieldvalue#7.4", text=text, button="Add", colID=colID, fldID=fldID, ln=ln, confirm=1) if fldvID not in [-1, "-1"] and confirm in [1, "1"]: output += write_outcome(ares) elif confirm in [1, "1"]: output += """<b><span class="info">Select a value to add and try again.</span></b>""" body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_modifyfield(colID, fldID, ln, content=output) def perform_addexistingfield(colID, ln, fldID=-1, fldvID=-1, fmeth='', callback='yes', confirm=-1): """form to add an existing field to a collection. colID - the collection to add the field to fldID - the field to add sel_ln - the language of the field""" subtitle = """<a name="6.2"></a><a name="7.2"></a><a name="8.2"></a>Add existing field to collection""" output = "" if fldID not in [-1, "-1"] and confirm in [1, "1"]: fldID = int(fldID) ares = add_col_fld(colID, fldID, fmeth, fldvID) colID = int(colID) lang = dict(get_languages()) res = get_def_name('', "field") col_dict = dict(get_def_name('', "collection")) fld_dict = dict(res) col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, fmeth))) fld_value = get_fld_value() fldv_dict = dict(map(lambda x: (x[0], x[1]), fld_value)) if fldvID: fldvID = int(fldvID) text = """ <span class="adminlabel">Field</span> <select name="fldID" class="admin_w200"> <option value="-1">- Select field -</option> """ for (id, var) in res: if fmeth == 'seo' or (fmeth != 'seo' and id not in col_fld): text += """<option value="%s" %s>%s</option> """ % (id, '', fld_dict[id]) text += """</select><br />""" output += createhiddenform(action="addexistingfield#6.2", text=text, button="Add", colID=colID, fmeth=fmeth, ln=ln, confirm=1) if fldID not in [-1, "-1"] and confirm in [1, "1"]: output += write_outcome(ares) elif fldID in [-1, "-1"] and confirm not in [-1, "-1"]: output += """<b><span class="info">Select a field.</span></b> """ body = [output] output = "<br />" + addadminbox(subtitle, body) if fmeth == "soo": return perform_showsortoptions(colID, ln, content=output) elif fmeth == "sew": return perform_showsearchfields(colID, ln, content=output) elif fmeth == "seo": return perform_showsearchoptions(colID, ln, content=output) def perform_showsortoptions(colID, ln, callback='yes', content='', confirm=-1): """show the sort fields of this collection..""" colID = int(colID) col_dict = dict(get_def_name('', "collection")) fld_dict = dict(get_def_name('', "field")) fld_type = get_sort_nametypes() subtitle = """<a name="8">8. Modify sort options for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.8">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) output = """<dl> <dt>Field actions (not related to this collection)</dt> <dd>Go to the BibIndex interface to modify the available sort options</dd> <dt>Collection specific actions <dd><a href="addexistingfield?colID=%s&ln=%s&fmeth=soo#8.2">Add sort option to collection</a></dd> <dd><a href="rearrangefield?colID=%s&ln=%s&fmeth=soo#8.2">Order sort options alphabetically</a></dd> </dl> """ % (colID, ln, colID, ln) header = ['', 'Sort option', 'Actions'] actions = [] sitelangs = get_languages() lang = dict(sitelangs) fld_type_list = fld_type.items() if len(get_col_fld(colID, 'soo')) > 0: res = get_col_fld(colID, 'soo') i = 0 for (fldID, fldvID, stype, score, score_fieldvalue) in res: move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>""" if i != 0: move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&ln=%s&id_1=%s&id_2=%s&fmeth=soo&rand=%s#8"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), CFG_SITE_URL) else: move += " " move += "</td><td>" i += 1 if i != len(res): move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&ln=%s&id_1=%s&id_2=%s&fmeth=soo&rand=%s#8"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i][0], random.randint(0, 1000), CFG_SITE_URL) move += """</td></tr></table>""" actions.append([move, fld_dict[int(fldID)]]) for col in [(('Remove sort option', 'removefield'),)]: actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s&fmeth=soo#8.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0])) for (str, function) in col[1:]: actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s&fmeth=soo#8.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str) output += tupletotable(header=header, tuple=actions) else: output += """No sort options exists for this collection""" output += content body = [output] if callback: return perform_editcollection(colID, ln, "perform_showsortoptions", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_showsearchfields(colID, ln, callback='yes', content='', confirm=-1): """show the search fields of this collection..""" colID = int(colID) col_dict = dict(get_def_name('', "collection")) fld_dict = dict(get_def_name('', "field")) fld_type = get_sort_nametypes() subtitle = """<a name="6">6. Modify search fields for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.6">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) output = """<dl> <dt>Field actions (not related to this collection)</dt> <dd>Go to the BibIndex interface to modify the available search fields</dd> <dt>Collection specific actions <dd><a href="addexistingfield?colID=%s&ln=%s&fmeth=sew#6.2">Add search field to collection</a></dd> <dd><a href="rearrangefield?colID=%s&ln=%s&fmeth=sew#6.2">Order search fields alphabetically</a></dd> </dl> """ % (colID, ln, colID, ln) header = ['', 'Search field', 'Actions'] actions = [] sitelangs = get_languages() lang = dict(sitelangs) fld_type_list = fld_type.items() if len(get_col_fld(colID, 'sew')) > 0: res = get_col_fld(colID, 'sew') i = 0 for (fldID, fldvID, stype, score, score_fieldvalue) in res: move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>""" if i != 0: move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&ln=%s&id_1=%s&id_2=%s&fmeth=sew&rand=%s#6"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), CFG_SITE_URL) else: move += " " move += "</td><td>" i += 1 if i != len(res): move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&ln=%s&id_1=%s&id_2=%s&fmeth=sew&rand=%s#6"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (CFG_SITE_URL, colID, ln, fldID, res[i][0], random.randint(0, 1000), CFG_SITE_URL) move += """</td></tr></table>""" actions.append([move, fld_dict[int(fldID)]]) for col in [(('Remove search field', 'removefield'),)]: actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s&fmeth=sew#6.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0])) for (str, function) in col[1:]: actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s#6.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str) output += tupletotable(header=header, tuple=actions) else: output += """No search fields exists for this collection""" output += content body = [output] if callback: return perform_editcollection(colID, ln, "perform_showsearchfields", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_showsearchoptions(colID, ln, callback='yes', content='', confirm=-1): """show the sort and search options of this collection..""" colID = int(colID) col_dict = dict(get_def_name('', "collection")) fld_dict = dict(get_def_name('', "field")) fld_type = get_sort_nametypes() subtitle = """<a name="7">7. Modify search options for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.7">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) output = """<dl> <dt>Field actions (not related to this collection)</dt> <dd>Go to the BibIndex interface to modify the available search options</dd> <dt>Collection specific actions <dd><a href="addexistingfield?colID=%s&ln=%s&fmeth=seo#7.2">Add search option to collection</a></dd> <dd><a href="rearrangefield?colID=%s&ln=%s&fmeth=seo#7.2">Order search options alphabetically</a></dd> </dl> """ % (colID, ln, colID, ln) header = ['', 'Search option', 'Actions'] actions = [] sitelangs = get_languages() lang = dict(sitelangs) fld_type_list = fld_type.items() fld_distinct = run_sql("SELECT distinct(id_field) FROM collection_field_fieldvalue WHERE type='seo' AND id_collection=%s ORDER by score desc", (colID, )) if len(fld_distinct) > 0: i = 0 for (id) in fld_distinct: fldID = id[0] col_fld = get_col_fld(colID, 'seo', fldID) move = "" if i != 0: move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&ln=%s&id_1=%s&id_2=%s&fmeth=seo&rand=%s#7"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fld_distinct[i - 1][0], random.randint(0, 1000), CFG_SITE_URL) else: move += " " i += 1 if i != len(fld_distinct): move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&ln=%s&id_1=%s&id_2=%s&fmeth=seo&rand=%s#7"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (CFG_SITE_URL, colID, ln, fldID, fld_distinct[i][0], random.randint(0, 1000), CFG_SITE_URL) actions.append([move, "%s" % fld_dict[fldID]]) for col in [(('Modify values', 'modifyfield'), ('Remove search option', 'removefield'),)]: actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s#7.3">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0])) for (str, function) in col[1:]: actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s&fmeth=seo#7.3">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str) output += tupletotable(header=header, tuple=actions) else: output += """No search options exists for this collection""" output += content body = [output] if callback: return perform_editcollection(colID, ln, "perform_showsearchoptions", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_modifyfield(colID, fldID, fldvID='', ln=CFG_SITE_LANG, content='', callback='yes', confirm=0): """Modify the fieldvalues for a field""" colID = int(colID) col_dict = dict(get_def_name('', "collection")) fld_dict = dict(get_def_name('', "field")) fld_type = get_sort_nametypes() fldID = int(fldID) subtitle = """<a name="7.3">Modify values for field '%s'</a>""" % (fld_dict[fldID]) output = """<dl> <dt>Value specific actions <dd><a href="addexistingfieldvalue?colID=%s&ln=%s&fldID=%s#7.4">Add existing value to search option</a></dd> <dd><a href="addnewfieldvalue?colID=%s&ln=%s&fldID=%s#7.4">Add new value to search option</a></dd> <dd><a href="rearrangefieldvalue?colID=%s&ln=%s&fldID=%s#7.4">Order values alphabetically</a></dd> </dl> """ % (colID, ln, fldID, colID, ln, fldID, colID, ln, fldID) header = ['', 'Value name', 'Actions'] actions = [] sitelangs = get_languages() lang = dict(sitelangs) fld_type_list = fld_type.items() col_fld = list(get_col_fld(colID, 'seo', fldID)) if len(col_fld) == 1 and col_fld[0][1] is None: output += """<b><span class="info">No values added for this search option yet</span></b>""" else: j = 0 for (fldID, fldvID, stype, score, score_fieldvalue) in col_fld: fieldvalue = get_fld_value(fldvID) move = "" if j != 0: move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&ln=%s&id_1=%s&id_fldvalue_1=%s&id_fldvalue_2=%s&rand=%s#7.3"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fldvID, col_fld[j - 1][1], random.randint(0, 1000), CFG_SITE_URL) else: move += " " j += 1 if j != len(col_fld): move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&ln=%s&id_1=%s&id_fldvalue_1=%s&id_fldvalue_2=%s&rand=%s#7.3"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fldvID, col_fld[j][1], random.randint(0, 1000), CFG_SITE_URL) if fieldvalue[0][1] != fieldvalue[0][2] and fldvID is not None: actions.append([move, "%s - %s" % (fieldvalue[0][1], fieldvalue[0][2])]) elif fldvID is not None: actions.append([move, "%s" % fieldvalue[0][1]]) move = '' for col in [(('Modify value', 'modifyfieldvalue'), ('Remove value', 'removefieldvalue'),)]: actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s&fldvID=%s&fmeth=seo#7.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, fldvID, col[0][0])) for (str, function) in col[1:]: actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fldID=%s&fldvID=%s#7.4">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, fldvID, str) output += tupletotable(header=header, tuple=actions) output += content body = [output] output = "<br />" + addadminbox(subtitle, body) if len(col_fld) == 0: output = content return perform_showsearchoptions(colID, ln, content=output) def perform_showoutputformats(colID, ln, callback='yes', content='', confirm=-1): """shows the outputformats of the current collection colID - the collection id.""" colID = int(colID) col_dict = dict(get_def_name('', "collection")) subtitle = """<a name="10">10. Modify output formats for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.10">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) output = """ <dl> <dt>Output format actions (not specific to the chosen collection) <dd>Go to the BibFormat interface to modify</dd> <dt>Collection specific actions <dd><a href="addexistingoutputformat?colID=%s&ln=%s#10.2">Add existing output format to collection</a></dd> </dl> """ % (colID, ln) header = ['', 'Code', 'Output format', 'Actions'] actions = [] col_fmt = get_col_fmt(colID) fmt_dict = dict(get_def_name('', "format")) i = 0 if len(col_fmt) > 0: for (id_format, colID_fld, code, score) in col_fmt: move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>""" if i != 0: move += """<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&ln=%s&type=format&id_1=%s&id_2=%s&rand=%s#10"><img border="0" src="%s/img/smallup.gif" title="Move format up"></a>""" % (CFG_SITE_URL, colID, ln, id_format, col_fmt[i - 1][0], random.randint(0, 1000), CFG_SITE_URL) else: move += " " move += "</td><td>" i += 1 if i != len(col_fmt): move += '<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&ln=%s&type=format&id_1=%s&id_2=%s&rand=%s#10"><img border="0" src="%s/img/smalldown.gif" title="Move format down"></a>' % (CFG_SITE_URL, colID, ln, id_format, col_fmt[i][0], random.randint(0, 1000), CFG_SITE_URL) move += """</td></tr></table>""" actions.append([move, code, fmt_dict[int(id_format)]]) for col in [(('Remove', 'removeoutputformat'),)]: actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fmtID=%s#10">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, id_format, col[0][0])) for (str, function) in col[1:]: actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&ln=%s&fmtID=%s#10">%s</a>' % (CFG_SITE_URL, function, colID, ln, id_format, str) output += tupletotable(header=header, tuple=actions) else: output += """No output formats exists for this collection""" output += content body = [output] if callback: return perform_editcollection(colID, ln, "perform_showoutputformats", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def external_collections_build_select(colID, external_collection): output = '<select name="state" class="admin_w200">' if external_collection.parser: max_state = 4 else: max_state = 2 num_selected = external_collection_get_state(external_collection, colID) for num in range(max_state): state_name = CFG_EXTERNAL_COLLECTION_STATES_NAME[num] if num == num_selected: selected = ' selected' else: selected = '' output += '<option value="%(num)d"%(selected)s>%(state_name)s</option>' % {'num': num, 'selected': selected, 'state_name': state_name} output += '</select>\n' return output def perform_manage_external_collections(colID, ln, callback='yes', content='', confirm=-1): """Show the interface to configure external collections to the user.""" colID = int(colID) subtitle = """<a name="11">11. Configuration of related external collections</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.11">?</a>]</small>""" % CFG_SITE_URL output = '<form action="update_external_collections" method="POST"><input type="hidden" name="colID" value="%(colID)d">' % {'colID': colID} table_header = ['External collection', 'Mode', 'Apply also to daughter collections?'] table_content = [] external_collections = external_collection_sort_engine_by_name(external_collections_dictionary.values()) for external_collection in external_collections: collection_name = external_collection.name select = external_collections_build_select(colID, external_collection) recurse = '<input type=checkbox name="recurse" value="%(collection_name)s">' % {'collection_name': collection_name} table_content.append([collection_name, select, recurse]) output += tupletotable(header=table_header, tuple=table_content) output += '<input class="adminbutton" type="submit" value="Modify"/>' output += '</form>' return addadminbox(subtitle, [output]) def perform_update_external_collections(colID, ln, state_list, recurse_list): colID = int(colID) changes = [] output = "" if not state_list: return 'Warning : No state found.<br />' + perform_manage_external_collections(colID, ln) external_collections = external_collection_sort_engine_by_name(external_collections_dictionary.values()) if len(external_collections) != len(state_list): return 'Warning : Size of state_list different from external_collections!<br />' + perform_manage_external_collections(colID, ln) for (external_collection, state) in zip(external_collections, state_list): state = int(state) collection_name = external_collection.name recurse = recurse_list and collection_name in recurse_list oldstate = external_collection_get_state(external_collection, colID) if oldstate != state or recurse: changes += external_collection_get_update_state_list(external_collection, colID, state, recurse) external_collection_apply_changes(changes) return output + '<br /><br />' + perform_manage_external_collections(colID, ln) def perform_showdetailedrecordoptions(colID, ln, callback='yes', content='', confirm=-1): """Show the interface to configure detailed record page to the user.""" colID = int(colID) subtitle = """<a name="12">12. Configuration of detailed record page</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.12">?</a>]</small>""" % CFG_SITE_URL output = '''<form action="update_detailed_record_options" method="post"> <table><tr><td> <input type="hidden" name="colID" value="%(colID)d"> <dl> <dt><b>Show tabs:</b></dt> <dd> ''' % {'colID': colID} for (tab_id, tab_info) in iteritems(get_detailed_page_tabs(colID)): if tab_id == 'comments' and \ not CFG_WEBCOMMENT_ALLOW_REVIEWS and \ not CFG_WEBCOMMENT_ALLOW_COMMENTS: continue check = '' output += '''<input type="checkbox" id="id%(tabid)s" name="tabs" value="%(tabid)s" %(check)s /> <label for="id%(tabid)s"> %(label)s</label><br /> ''' % {'tabid':tab_id, 'check':((tab_info['visible'] and 'checked="checked"') or ''), 'label':tab_info['label']} output += '</dd></dl></td><td>' output += '</td></tr></table><input class="adminbutton" type="submit" value="Modify"/>' output += '''<input type="checkbox" id="recurse" name="recurse" value="1" /> <label for="recurse"> Also apply to subcollections</label>''' output += '</form>' return addadminbox(subtitle, [output]) def perform_update_detailed_record_options(colID, ln, tabs, recurse): """Update the preferences for the tab to show/hide in the detailed record page.""" colID = int(colID) changes = [] output = '<b><span class="info">Operation successfully completed.</span></b>' if '' in tabs: tabs.remove('') tabs.append('metadata') def update_settings(colID, tabs, recurse): run_sql("DELETE FROM collectiondetailedrecordpagetabs WHERE id_collection=%s", (colID, )) run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \ " SET id_collection=%s, tabs=%s", (colID, ';'.join(tabs))) ## for enabled_tab in tabs: # run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \ # " SET id_collection='%s', tabs='%s'" % (colID, ';'.join(tabs))) if recurse: for descendant_id in get_collection_descendants(colID): update_settings(descendant_id, tabs, recurse) update_settings(colID, tabs, recurse) # for colID in colIDs: # run_sql("DELETE FROM collectiondetailedrecordpagetabs WHERE id_collection='%s'" % colID) # for enabled_tab in tabs: # run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \ # " SET id_collection='%s', tabs='%s'" % (colID, ';'.join(tabs))) #if callback: return perform_editcollection(colID, ln, "perform_modifytranslations", '<br /><br />' + output + '<br /><br />' + \ perform_showdetailedrecordoptions(colID, ln)) #else: # return addadminbox(subtitle, body) #return output + '<br /><br />' + perform_showdetailedrecordoptions(colID, ln) def perform_addexistingoutputformat(colID, ln, fmtID=-1, callback='yes', confirm=-1): """form to add an existing output format to a collection. colID - the collection the format should be added to fmtID - the format to add.""" subtitle = """<a name="10.2"></a>Add existing output format to collection""" output = "" if fmtID not in [-1, "-1"] and confirm in [1, "1"]: ares = add_col_fmt(colID, fmtID) colID = int(colID) res = get_def_name('', "format") fmt_dict = dict(res) col_dict = dict(get_def_name('', "collection")) col_fmt = get_col_fmt(colID) col_fmt = dict(map(lambda x: (x[0], x[2]), col_fmt)) if len(res) > 0: text = """ <span class="adminlabel">Output format</span> <select name="fmtID" class="admin_w200"> <option value="-1">- Select output format -</option> """ for (id, name) in res: if id not in col_fmt: text += """<option value="%s" %s>%s</option> """ % (id, id == int(fmtID) and 'selected="selected"' or '', name) text += """</select><br /> """ output += createhiddenform(action="addexistingoutputformat#10.2", text=text, button="Add", colID=colID, ln=ln, confirm=1) else: output = """No existing output formats to add, please create a new one.""" if fmtID not in [-1, "-1"] and confirm in [1, "1"]: output += write_outcome(ares) elif fmtID in [-1, "-1"] and confirm not in [-1, "-1"]: output += """<b><span class="info">Please select output format.</span></b>""" body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_showoutputformats(colID, ln, content=output) def perform_deleteoutputformat(colID, ln, fmtID=-1, callback='yes', confirm=-1): """form to delete an output format not in use. colID - the collection id of the current collection. fmtID - the format id to delete.""" subtitle = """<a name="10.3"></a>Delete an unused output format""" output = """ <dl> <dd>Deleting an output format will also delete the translations associated.</dd> </dl> """ colID = int(colID) if fmtID not in [-1, "-1"] and confirm in [1, "1"]: fmt_dict = dict(get_def_name('', "format")) old_colNAME = fmt_dict[int(fmtID)] ares = delete_fmt(int(fmtID)) res = get_def_name('', "format") fmt_dict = dict(res) col_dict = dict(get_def_name('', "collection")) col_fmt = get_col_fmt() col_fmt = dict(map(lambda x: (x[0], x[2]), col_fmt)) if len(res) > 0: text = """ <span class="adminlabel">Output format</span> <select name="fmtID" class="admin_w200"> """ text += """<option value="-1">- Select output format -""" for (id, name) in res: if id not in col_fmt: text += """<option value="%s" %s>%s""" % (id, id == int(fmtID) and 'selected="selected"' or '', name) text += "</option>" text += """</select><br />""" output += createhiddenform(action="deleteoutputformat#10.3", text=text, button="Delete", colID=colID, ln=ln, confirm=0) if fmtID not in [-1, "-1"]: fmtID = int(fmtID) if confirm in [0, "0"]: text = """<b>Do you want to delete the output format '%s'.</b> """ % fmt_dict[fmtID] output += createhiddenform(action="deleteoutputformat#10.3", text=text, button="Confirm", colID=colID, fmtID=fmtID, ln=ln, confirm=1) elif confirm in [1, "1"]: output += write_outcome(ares) elif confirm not in [-1, "-1"]: output += """<b><span class="info">Choose a output format to delete.</span></b> """ body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_showoutputformats(colID, ln, content=output) def perform_removeoutputformat(colID, ln, fmtID='', callback='yes', confirm=0): """form to remove an output format from a collection. colID - the collection id of the current collection. fmtID - the format id. """ subtitle = """<a name="10.5"></a>Remove output format""" output = "" col_dict = dict(get_def_name('', "collection")) fmt_dict = dict(get_def_name('', "format")) if colID and fmtID: colID = int(colID) fmtID = int(fmtID) if confirm in ["0", 0]: text = """Do you want to remove the output format '%s' from the collection '%s'.""" % (fmt_dict[fmtID], col_dict[colID]) output += createhiddenform(action="removeoutputformat#10.5", text=text, button="Confirm", colID=colID, fmtID=fmtID, confirm=1) elif confirm in ["1", 1]: res = remove_fmt(colID, fmtID) output += write_outcome(res) body = [output] output = "<br />" + addadminbox(subtitle, body) return perform_showoutputformats(colID, ln, content=output) def perform_index(colID=1, ln=CFG_SITE_LANG, mtype='', content='', confirm=0): """The index method, calling methods to show the collection tree, create new collections and add collections to tree. """ subtitle = "Overview" colID = int(colID) col_dict = dict(get_def_name('', "collection")) output = "" fin_output = "" if 1 not in col_dict: res = add_col(CFG_SITE_NAME, '') if res: fin_output += """<b><span class="info">Created root collection.</span></b><br />""" else: return "Cannot create root collection, please check database." if CFG_SITE_NAME != run_sql("SELECT name from collection WHERE id=1")[0][0]: res = run_sql("update collection set name=%s where id=1", (CFG_SITE_NAME, )) if res: fin_output += """<b><span class="info">The name of the root collection has been modified to be the same as the %(sitename)s installation name given prior to installing %(sitename)s.</span><b><br />""" % {'sitename' : CFG_SITE_NAME} else: return "Error renaming root collection." fin_output += """ <table> <tr> <td>0. <small><a href="{cfg_site_url}/admin/websearch/websearchadmin.py?colID={col_id}&ln={ln}&mtype=perform_showall">Show all</a></small></td> <td>1. <small><a href="{cfg_site_url}/admin/websearch/websearchadmin.py?colID={col_id}&ln={ln}&mtype=perform_addcollection">Create new collection</a></small></td> <td>2. <small><a href="{cfg_site_url}/admin/websearch/websearchadmin.py?colID={col_id}&ln={ln}&mtype=perform_addcollectiontotree">Attach collection to tree</a></small></td> <td>3. <small><a href="{cfg_site_url}/admin/websearch/websearchadmin.py?colID={col_id}&ln={ln}&mtype=perform_modifycollectiontree">Modify collection tree</a></small></td> <td>4. <small><a href="{cfg_site_url}/admin/websearch/websearchadmin.py?colID={col_id}&ln={ln}&mtype=perform_checkwebcollstatus">Webcoll Status</a></small></td> </tr><tr> <td>5. <small><a href="{cfg_site_url}/admin/websearch/websearchadmin.py?colID={col_id}&ln={ln}&mtype=perform_checkcollectionstatus">Collection Status</a></small></td> <td>6. <small><a href="{cfg_site_url}/admin/websearch/websearchadmin.py?colID={col_id}&ln={ln}&mtype=perform_checkexternalcollections">Check external collections</a></small></td> <td>7. <small><a href="{cfg_site_url}/help/admin/websearch-admin-guide?ln={col_id}">Guide</a></small></td> </tr> </table> """.format(cfg_site_url=CFG_SITE_URL, col_id=colID, ln=ln) if mtype == "": fin_output += """<br /><br /><b><span class="info">To manage the collections, select an item from the menu.</span><b><br />""" if mtype == "perform_addcollection" and content: fin_output += content elif mtype == "perform_addcollection" or mtype == "perform_showall": fin_output += perform_addcollection(colID=colID, ln=ln, callback='') fin_output += "<br />" if mtype == "perform_addcollectiontotree" and content: fin_output += content elif mtype == "perform_addcollectiontotree" or mtype == "perform_showall": fin_output += perform_addcollectiontotree(colID=colID, ln=ln, callback='') fin_output += "<br />" if mtype == "perform_modifycollectiontree" and content: fin_output += content elif mtype == "perform_modifycollectiontree" or mtype == "perform_showall": fin_output += perform_modifycollectiontree(colID=colID, ln=ln, callback='') fin_output += "<br />" if mtype == "perform_checkwebcollstatus" and content: fin_output += content elif mtype == "perform_checkwebcollstatus" or mtype == "perform_showall": fin_output += perform_checkwebcollstatus(colID, ln, callback='') if mtype == "perform_checkcollectionstatus" and content: fin_output += content elif mtype == "perform_checkcollectionstatus" or mtype == "perform_showall": fin_output += perform_checkcollectionstatus(colID, ln, callback='') if mtype == "perform_checkexternalcollections" and content: fin_output += content elif mtype == "perform_checkexternalcollections" or mtype == "perform_showall": fin_output += perform_checkexternalcollections(colID, ln, callback='') body = [fin_output] return addadminbox('<b>Menu</b>', body) def show_coll_not_in_tree(colID, ln, col_dict): """Returns collections not in tree""" tree = get_col_tree(colID) in_tree = {} output = "These collections are not in the tree, and should be added:<br />" for (id, up, down, dad, reltype) in tree: in_tree[id] = 1 in_tree[dad] = 1 res = run_sql("SELECT id from collection") if len(res) != len(in_tree): for id in res: if id[0] not in in_tree: output += """<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&ln=%s" title="Edit collection">%s</a> , """ % (CFG_SITE_URL, id[0], ln, col_dict[id[0]]) output += "<br /><br />" else: output = "" return output def create_colltree(tree, col_dict, colID, ln, move_from='', move_to='', rtype='', edit=''): """Creates the presentation of the collection tree, with the buttons for modifying it. tree - the tree to present, from get_tree() col_dict - the name of the collections in a dictionary colID - the collection id to start with move_from - if a collection to be moved has been chosen move_to - the collection which should be set as father of move_from rtype - the type of the tree, regular or virtual edit - if the method should output the edit buttons.""" if move_from: move_from_rtype = move_from[0] move_from_id = int(move_from[1:len(move_from)]) tree_from = get_col_tree(colID, move_from_rtype) tree_to = get_col_tree(colID, rtype) tables = 0 tstack = [] i = 0 text = """ <table border ="0" cellspacing="0" cellpadding="0">""" for i in range(0, len(tree)): id_son = tree[i][0] up = tree[i][1] down = tree[i][2] dad = tree[i][3] reltype = tree[i][4] tmove_from = "" j = i while j > 0: j = j - 1 try: if tstack[j][1] == dad: table = tstack[j][2] for k in range(0, tables - table): tables = tables - 1 text += """</table></td></tr> """ break except StandardError as e: pass text += """<tr><td> """ if i > 0 and tree[i][1] == 0: tables = tables + 1 text += """</td><td></td><td></td><td></td><td><table border="0" cellspacing="0" cellpadding="0"><tr><td> """ if i == 0: tstack.append((id_son, dad, 1)) else: tstack.append((id_son, dad, tables)) if up == 1 and edit: text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&ln=%s&move_up=%s&rtype=%s#%s"><img border="0" src="%s/img/smallup.gif" title="Move collection up"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL) else: text += """ """ text += "</td><td>" if down == 1 and edit: text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&ln=%s&move_down=%s&rtype=%s#%s"><img border="0" src="%s/img/smalldown.gif" title="Move collection down"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL) else: text += """ """ text += "</td><td>" if edit: if move_from and move_to: tmove_from = move_from move_from = '' if not (move_from == "" and i == 0) and not (move_from != "" and int(move_from[1:len(move_from)]) == i and rtype == move_from[0]): check = "true" if move_from: #if tree_from[move_from_id][0] == tree_to[i][0] or not check_col(tree_to[i][0], tree_from[move_from_id][0]): # check = '' #elif not check_col(tree_to[i][0], tree_from[move_from_id][0]): # check = '' #if not check and (tree_to[i][0] == 1 and tree_from[move_from_id][3] == tree_to[i][0] and move_from_rtype != rtype): # check = "true" if check: text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&ln=%s&move_from=%s&move_to=%s%s&rtype=%s#tree"><img border="0" src="%s/img/move_to.gif" title="Move '%s' to '%s'"></a> """ % (CFG_SITE_URL, colID, ln, move_from, rtype, i, rtype, CFG_SITE_URL, col_dict[tree_from[int(move_from[1:len(move_from)])][0]], col_dict[tree_to[i][0]]) else: try: text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&ln=%s&move_from=%s%s&rtype=%s#%s"><img border="0" src="%s/img/move_from.gif" title="Move '%s' from this location."></a>""" % (CFG_SITE_URL, colID, ln, rtype, i, rtype, tree[i][0], CFG_SITE_URL, col_dict[tree[i][0]]) except KeyError: pass else: text += """<img border="0" src="%s/img/white_field.gif"> """ % CFG_SITE_URL else: text += """<img border="0" src="%s/img/white_field.gif"> """ % CFG_SITE_URL text += """ </td> <td>""" if edit: try: text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&ln=%s&delete=%s&rtype=%s#%s"><img border="0" src="%s/img/iconcross.gif" title="Remove colletion from tree"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL) except KeyError: pass elif i != 0: text += """<img border="0" src="%s/img/white_field.gif"> """ % CFG_SITE_URL text += """</td><td> """ if tmove_from: move_from = tmove_from try: text += """<a name="%s"></a>%s<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&ln=%s" title="Edit collection">%s</a>%s%s%s""" % (tree[i][0], (reltype=="v" and '<i>' or ''), CFG_SITE_URL, tree[i][0], ln, col_dict[id_son], (move_to=="%s%s" %(rtype, i) and ' <img border="0" src="%s/img/move_to.gif">' % CFG_SITE_URL or ''), (move_from=="%s%s" % (rtype, i) and ' <img border="0" src="%s/img/move_from.gif">' % CFG_SITE_URL or ''), (reltype=="v" and '</i>' or '')) except KeyError: pass text += """</td></tr> """ while tables > 0: text += """</table></td></tr> """ tables = tables - 1 text += """</table>""" return text def perform_deletecollection(colID, ln, confirm=-1, callback='yes'): """form to delete a collection colID - id of collection """ subtitle ='' output = """ <span class="warning"> <strong> <dl> <dt>WARNING:</dt> <dd>When deleting a collection, you also deletes all data related to the collection like translations, relations to other collections and information about which rank methods to use. <br />For more information, please go to the <a title="See guide" href="%s/help/admin/websearch-admin-guide">WebSearch guide</a> and read the section regarding deleting a collection.</dd> </dl> </strong> </span> """ % CFG_SITE_URL col_dict = dict(get_def_name('', "collection")) if colID != 1 and colID and int(colID) in col_dict: colID = int(colID) subtitle = """<a name="4">4. Delete collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.4">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) res = run_sql("SELECT id_dad,id_son,type,score from collection_collection WHERE id_dad=%s", (colID, )) res2 = run_sql("SELECT id_dad,id_son,type,score from collection_collection WHERE id_son=%s", (colID, )) if not res and not res2: if confirm in ["-1", -1]: text = """Do you want to delete this collection.""" output += createhiddenform(action="deletecollection#4", text=text, colID=colID, button="Delete", confirm=0) elif confirm in ["0", 0]: text = """Are you sure you want to delete this collection.""" output += createhiddenform(action="deletecollection#4", text=text, colID=colID, button="Confirm", confirm=1) elif confirm in ["1", 1]: result = delete_col(colID) if not result: raise Exception else: output = """<b><span class="info">Can not delete a collection that is a part of the collection tree, remove collection from the tree and try again.</span></b>""" else: subtitle = """4. Delete collection""" output = """<b><span class="info">Not possible to delete the root collection</span></b>""" body = [output] if callback: return perform_editcollection(colID, ln, "perform_deletecollection", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_editcollection(colID=1, ln=CFG_SITE_LANG, mtype='', content=''): """interface to modify a collection. this method is calling other methods which again is calling this and sending back the output of the method. if callback, the method will call perform_editcollection, if not, it will just return its output. colID - id of the collection mtype - the method that called this method. content - the output from that method.""" colID = int(colID) col_dict = dict(get_def_name('', "collection")) if colID not in col_dict: return """<b><span class="info">Collection deleted.</span></b> """ fin_output = """ <table> <tr> <td><b>Menu</b></td> </tr> <tr> <td>0. <small><a href="editcollection?colID=%s&ln=%s">Show all</a></small></td> <td>1. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_modifydbquery">Modify collection query</a></small></td> <td>2. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_modifyrestricted">Modify access restrictions</a></small></td> <td>3. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_modifytranslations">Modify translations</a></small></td> <td>4. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_deletecollection">Delete collection</a></small></td> </tr><tr> <td>5. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_showportalboxes">Modify portalboxes</a></small></td> <td>6. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_showsearchfields#6">Modify search fields</a></small></td> <td>7. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_showsearchoptions#7">Modify search options</a></small></td> <td>8. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_showsortoptions#8">Modify sort options</a></small></td> <td>9. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_modifyrankmethods#9">Modify rank options</a></small></td> </tr><tr> <td>10. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_showoutputformats#10">Modify output formats</a></small></td> <td>11. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_manage_external_collections#11">Configuration of related external collections</a></small></td> <td>12. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_showdetailedrecordoptions#12">Detailed record page options</a></small></td> </tr> </table> """ % (colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln) if mtype == "perform_modifydbquery" and content: fin_output += content elif mtype == "perform_modifydbquery" or not mtype: fin_output += perform_modifydbquery(colID, ln, callback='') if mtype == "perform_modifyrestricted" and content: fin_output += content elif mtype == "perform_modifyrestricted" or not mtype: fin_output += perform_modifyrestricted(colID, ln, callback='') if mtype == "perform_modifytranslations" and content: fin_output += content elif mtype == "perform_modifytranslations" or not mtype: fin_output += perform_modifytranslations(colID, ln, callback='') if mtype == "perform_deletecollection" and content: fin_output += content elif mtype == "perform_deletecollection" or not mtype: fin_output += perform_deletecollection(colID, ln, callback='') if mtype == "perform_showportalboxes" and content: fin_output += content elif mtype == "perform_showportalboxes" or not mtype: fin_output += perform_showportalboxes(colID, ln, callback='') if mtype == "perform_showsearchfields" and content: fin_output += content elif mtype == "perform_showsearchfields" or not mtype: fin_output += perform_showsearchfields(colID, ln, callback='') if mtype == "perform_showsearchoptions" and content: fin_output += content elif mtype == "perform_showsearchoptions" or not mtype: fin_output += perform_showsearchoptions(colID, ln, callback='') if mtype == "perform_showsortoptions" and content: fin_output += content elif mtype == "perform_showsortoptions" or not mtype: fin_output += perform_showsortoptions(colID, ln, callback='') if mtype == "perform_modifyrankmethods" and content: fin_output += content elif mtype == "perform_modifyrankmethods" or not mtype: fin_output += perform_modifyrankmethods(colID, ln, callback='') if mtype == "perform_showoutputformats" and content: fin_output += content elif mtype == "perform_showoutputformats" or not mtype: fin_output += perform_showoutputformats(colID, ln, callback='') if mtype == "perform_manage_external_collections" and content: fin_output += content elif mtype == "perform_manage_external_collections" or not mtype: fin_output += perform_manage_external_collections(colID, ln, callback='') if mtype == "perform_showdetailedrecordoptions" and content: fin_output += content elif mtype == "perform_showdetailedrecordoptions" or not mtype: fin_output += perform_showdetailedrecordoptions(colID, ln, callback='') return addadminbox("Overview of edit options for collection '%s'" % col_dict[colID], [fin_output]) def perform_checkwebcollstatus(colID, ln, confirm=0, callback='yes'): """Check status of the collection tables with respect to the webcoll cache.""" subtitle = """<a name="11"></a>Webcoll Status [<a href="%s/help/admin/websearch-admin-guide#5">?</a>]""" % CFG_SITE_URL output = "" colID = int(colID) col_dict = dict(get_def_name('', "collection")) output += """<br /><b>Last updates:</b><br />""" collection_table_update_time = "" collection_web_update_time = "" collection_table_update_time = get_table_update_time('collection') output += "Collection table last updated: %s<br />" % collection_table_update_time try: file = open("%s/collections/last_updated" % CFG_CACHEDIR) collection_web_update_time = file.readline().strip() output += "Collection cache last updated: %s<br />" % collection_web_update_time file.close() except: pass # reformat collection_web_update_time to the format suitable for comparisons try: collection_web_update_time = strftime("%Y-%m-%d %H:%M:%S", time.strptime(collection_web_update_time, "%d %b %Y %H:%M:%S")) except ValueError as e: pass if collection_table_update_time > collection_web_update_time: output += """<br /><b><span class="info">Warning: The collections have been modified since last time Webcoll was executed, to process the changes, Webcoll must be executed.</span></b><br />""" header = ['ID', 'Name', 'Time', 'Status', 'Progress'] actions = [] output += """<br /><b>Last BibSched tasks:</b><br />""" res = run_sql("""select id, proc, host, "user", runtime, sleeptime, arguments, status, progress from "schTASK" where proc='webcoll' and runtime< now() ORDER by runtime""") if len(res) > 0: (id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[len(res) - 1] webcoll__update_time = runtime actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')]) else: actions.append(['', 'webcoll', '', '', 'Not executed yet']) res = run_sql("""select id, proc, host, "user", runtime, sleeptime, arguments, status, progress from "schTASK" where proc='bibindex' and runtime< now() ORDER by runtime""") if len(res) > 0: (id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[len(res) - 1] actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')]) else: actions.append(['', 'bibindex', '', '', 'Not executed yet']) output += tupletotable(header=header, tuple=actions) output += """<br /><b>Next scheduled BibSched run:</b><br />""" actions = [] res = run_sql("""select id, proc, host, "user", runtime, sleeptime, arguments, status, progress from "schTASK" where proc='webcoll' and runtime > now() ORDER by runtime""") webcoll_future = "" if len(res) > 0: (id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[0] webcoll__update_time = runtime actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')]) webcoll_future = "yes" else: actions.append(['', 'webcoll', '', '', 'Not scheduled']) res = run_sql("""select id, proc, host, "user", runtime, sleeptime, arguments, status, progress from "schTASK" where proc='bibindex' and runtime > now() ORDER by runtime""") bibindex_future = "" if len(res) > 0: (id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[0] actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')]) bibindex_future = "yes" else: actions.append(['', 'bibindex', '', '', 'Not scheduled']) output += tupletotable(header=header, tuple=actions) if webcoll_future == "": output += """<br /><b><span class="info">Warning: Webcoll is not scheduled for a future run by bibsched, any updates to the collection will not be processed.</span></b><br />""" if bibindex_future == "": output += """<br /><b><span class="info">Warning: Bibindex is not scheduled for a future run by bibsched, any updates to the records will not be processed.</span></b><br />""" body = [output] if callback: return perform_index(colID, ln, "perform_checkwebcollstatus", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_modifyrestricted(colID, ln, rest='', callback='yes', confirm=-1): """modify which apache group is allowed to access the collection. rest - the groupname""" subtitle = '' output = "" col_dict = dict(get_def_name('', "collection")) action_id = acc_get_action_id(VIEWRESTRCOLL) if colID and int(colID) in col_dict: colID = int(colID) subtitle = """<a name="2">2. Modify access restrictions for collection '%s'</a> <small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.2">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL) output = """<p>Please note that Invenio versions greater than <em>0.92.1</em> manage collection restriction via the standard <strong><a href="/admin/webaccess/webaccessadmin.py/showactiondetails?id_action=%i">WebAccess Admin Interface</a></strong> (action '%s').</p> """ % (action_id, VIEWRESTRCOLL) body = [output] if callback: return perform_editcollection(colID, ln, "perform_modifyrestricted", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_checkcollectionstatus(colID, ln, confirm=0, callback='yes'): """Check the configuration of the collections.""" from invenio.legacy.search_engine import collection_restricted_p, restricted_collection_cache subtitle = """<a name="11"></a>Collection Status [<a href="%s/help/admin/websearch-admin-guide#6">?</a>]""" % CFG_SITE_URL output = "" colID = int(colID) col_dict = dict(get_def_name('', "collection")) collections = run_sql("SELECT id, name, dbquery, nbrecs FROM collection " "ORDER BY id") header = ['ID', 'Name','Query', 'Subcollections', 'Restricted', 'Hosted', 'I18N', 'Status', 'Number of records'] rnk_list = get_def_name('', "rnkMETHOD") actions = [] restricted_collection_cache.recreate_cache_if_needed() for (id, name, dbquery, nbrecs) in collections: reg_sons = col_has_son(id, 'r') vir_sons = col_has_son(id, 'v') status = "" hosted = "" if str(dbquery).startswith("hostedcollection:"): hosted = """<b><span class="info">Yes</span></b>""" else: hosted = """<b><span class="info">No</span></b>""" langs = run_sql("SELECT ln from collectionname where id_collection=%s", (id, )) i8n = "" if len(langs) > 0: for lang in langs: i8n += "%s, " % lang else: i8n = """<b><span class="info">None</span></b>""" if reg_sons and dbquery: status = """<b><span class="warning">1:Conflict</span></b>""" elif not dbquery and not reg_sons: status = """<b><span class="warning">2:Empty</span></b>""" if (reg_sons or vir_sons): subs = """<b><span class="info">Yes</span></b>""" else: subs = """<b><span class="info">No</span></b>""" if dbquery is None: dbquery = """<b><span class="info">No</span></b>""" restricted = collection_restricted_p(name, recreate_cache_if_needed=False) if restricted: restricted = """<b><span class="warning">Yes</span></b>""" if status: status += """<b><span class="warning">,3:Restricted</span></b>""" else: status += """<b><span class="warning">3:Restricted</span></b>""" else: restricted = """<b><span class="info">No</span></b>""" if status == "": status = """<b><span class="info">OK</span></b>""" actions.append([id, """<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&ln=%s">%s</a>""" % (CFG_SITE_URL, id, ln, name), dbquery, subs, restricted, hosted, i8n, status, nbrecs]) output += tupletotable(header=header, tuple=actions) body = [output] return addadminbox(subtitle, body) if callback: return perform_index(colID, ln, "perform_checkcollectionstatus", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def perform_checkexternalcollections(colID, ln, icl=None, update="", confirm=0, callback='yes'): """Check the external collections for inconsistencies.""" subtitle = """<a name="7"></a>Check external collections [<a href="%s/help/admin/websearch-admin-guide#7">?</a>]""" % CFG_SITE_URL output = "" colID = int(colID) if icl: if update == "add": # icl : the "inconsistent list" comes as a string, it has to be converted back into a list icl = eval(icl) #icl = icl[1:-1].split(',') for collection in icl: #collection = str(collection[1:-1]) query_select = "SELECT name FROM externalcollection WHERE name like '%(name)s';" % {'name': collection} results_select = run_sql(query_select) if not results_select: query_insert = "INSERT INTO externalcollection (name) VALUES ('%(name)s');" % {'name': collection} run_sql(query_insert) output += """<br /><span class=info>New collection \"%s\" has been added to the database table \"externalcollection\".</span><br />""" % (collection) else: output += """<br /><span class=info>Collection \"%s\" has already been added to the database table \"externalcollection\" or was already there.</span><br />""" % (collection) elif update == "del": # icl : the "inconsistent list" comes as a string, it has to be converted back into a list icl = eval(icl) #icl = icl[1:-1].split(',') for collection in icl: #collection = str(collection[1:-1]) query_select = "SELECT id FROM externalcollection WHERE name like '%(name)s';" % {'name': collection} results_select = run_sql(query_select) if results_select: query_delete = "DELETE FROM externalcollection WHERE id like '%(id)s';" % {'id': results_select[0][0]} query_delete_states = "DELETE FROM collection_externalcollection WHERE id_externalcollection like '%(id)s';" % {'id': results_select[0][0]} run_sql(query_delete) run_sql(query_delete_states) output += """<br /><span class=info>Collection \"%s\" has been deleted from the database table \"externalcollection\".</span><br />""" % (collection) else: output += """<br /><span class=info>Collection \"%s\" has already been delete from the database table \"externalcollection\" or was never there.</span><br />""" % (collection) external_collections_file = [] external_collections_db = [] for coll in external_collections_dictionary.values(): external_collections_file.append(coll.name) external_collections_file.sort() query = """SELECT name from externalcollection""" results = run_sql(query) for result in results: external_collections_db.append(result[0]) external_collections_db.sort() number_file = len(external_collections_file) number_db = len(external_collections_db) if external_collections_file == external_collections_db: output += """<br /><span class="info">External collections are consistent.</span><br /><br /> - database table \"externalcollection\" has %(number_db)s collections<br /> - configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections""" % { "number_db" : number_db, "number_file" : number_file} elif len(external_collections_file) > len(external_collections_db): external_collections_diff = list(set(external_collections_file) - set(external_collections_db)) external_collections_db.extend(external_collections_diff) external_collections_db.sort() if external_collections_file == external_collections_db: output += """<br /><span class="warning">There is an inconsistency:</span><br /><br /> - database table \"externalcollection\" has %(number_db)s collections (<span class="warning">missing: %(diff)s</span>)<br /> - configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections <br /><br /><a href="%(site_url)s/admin/websearch/websearchadmin.py/checkexternalcollections?colID=%(colID)s&icl=%(diff)s&update=add&ln=%(ln)s"> Click here</a> to update your database adding the missing collections. If the problem persists please check your configuration manually.""" % { "number_db" : number_db, "number_file" : number_file, "diff" : external_collections_diff, "site_url" : CFG_SITE_URL, "colID" : colID, "ln" : ln} else: output += """<br /><span class="warning">There is an inconsistency:</span><br /><br /> - database table \"externalcollection\" has %(number_db)s collections<br /> - configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections <br /><br /><span class="warning">The external collections do not match.</span> <br />To fix the problem please check your configuration manually.""" % { "number_db" : number_db, "number_file" : number_file} elif len(external_collections_file) < len(external_collections_db): external_collections_diff = list(set(external_collections_db) - set(external_collections_file)) external_collections_file.extend(external_collections_diff) external_collections_file.sort() if external_collections_file == external_collections_db: output += """<br /><span class="warning">There is an inconsistency:</span><br /><br /> - database table \"externalcollection\" has %(number_db)s collections (<span class="warning">extra: %(diff)s</span>)<br /> - configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections <br /><br /><a href="%(site_url)s/admin/websearch/websearchadmin.py/checkexternalcollections?colID=%(colID)s&icl=%(diff)s&update=del&ln=%(ln)s"> Click here</a> to force remove the extra collections from your database (warning: use with caution!). If the problem persists please check your configuration manually.""" % { "number_db" : number_db, "number_file" : number_file, "diff" : external_collections_diff, "site_url" : CFG_SITE_URL, "colID" : colID, "ln" : ln} else: output += """<br /><span class="warning">There is an inconsistency:</span><br /><br /> - database table \"externalcollection\" has %(number_db)s collections<br /> - configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections <br /><br /><span class="warning">The external collections do not match.</span> <br />To fix the problem please check your configuration manually.""" % { "number_db" : number_db, "number_file" : number_file} else: output += """<br /><span class="warning">There is an inconsistency:</span><br /><br /> - database table \"externalcollection\" has %(number_db)s collections<br /> - configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections <br /><br /><span class="warning">The number of external collections is the same but the collections do not match.</span> <br />To fix the problem please check your configuration manually.""" % { "number_db" : number_db, "number_file" : number_file} body = [output] return addadminbox(subtitle, body) if callback: return perform_index(colID, ln, "perform_checkexternalcollections", addadminbox(subtitle, body)) else: return addadminbox(subtitle, body) def col_has_son(colID, rtype='r'): """Return True if the collection has at least one son.""" return run_sql("SELECT id_son FROM collection_collection WHERE id_dad=%s and type=%s LIMIT 1", (colID, rtype)) != () def get_col_tree(colID, rtype=''): """Returns a presentation of the tree as a list. TODO: Add loop detection colID - startpoint for the tree rtype - get regular or virtual part of the tree""" try: colID = int(colID) stack = [colID] ssize = 0 tree = [(colID, 0, 0, colID, 'r')] while len(stack) > 0: ccolID = stack.pop() if ccolID == colID and rtype: res = run_sql("SELECT id_son, score, type FROM collection_collection WHERE id_dad=%s AND type=%s ORDER BY score DESC,id_son", (ccolID, rtype)) else: res = run_sql("SELECT id_son, score, type FROM collection_collection WHERE id_dad=%s ORDER BY score DESC,id_son", (ccolID, )) ssize += 1 ntree = [] for i in range(0, len(res)): id_son = res[i][0] score = res[i][1] rtype = res[i][2] stack.append(id_son) if i == (len(res) - 1): up = 0 else: up = 1 if i == 0: down = 0 else: down = 1 ntree.insert(0, (id_son, up, down, ccolID, rtype)) tree = tree[0:ssize] + ntree + tree[ssize:len(tree)] return tree except StandardError as e: register_exception() return () def add_col_dad_son(add_dad, add_son, rtype): """Add a son to a collection (dad) add_dad - add to this collection id add_son - add this collection id rtype - either regular or virtual""" try: res = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s ORDER BY score ASC", (add_dad, )) highscore = 0 for score in res: if int(score[0]) > highscore: highscore = int(score[0]) highscore += 1 res = run_sql("INSERT INTO collection_collection(id_dad,id_son,score,type) values(%s,%s,%s,%s)", (add_dad, add_son, highscore, rtype)) return (1, highscore) except StandardError as e: register_exception() return (0, e) def compare_on_val(first, second): """Compare the two values""" return cmp(first[1], second[1]) def get_col_fld(colID=-1, type = '', id_field=''): """Returns either all portalboxes associated with a collection, or based on either colID or language or both. colID - collection id ln - language id""" sql = "SELECT id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue, field WHERE id_field=field.id" params = [] if colID > -1: sql += " AND id_collection=%s" params.append(colID) if id_field: sql += " AND id_field=%s" params.append(id_field) if type: sql += " AND type=%s" params.append(type) sql += " ORDER BY type, score desc, score_fieldvalue desc" res = run_sql(sql, tuple(params)) return res def get_col_pbx(colID=-1, ln='', position = ''): """Returns either all portalboxes associated with a collection, or based on either colID or language or both. colID - collection id ln - language id""" sql = "SELECT id_portalbox, id_collection, ln, score, position, title, body FROM collection_portalbox, portalbox WHERE id_portalbox = portalbox.id" params = [] if colID > -1: sql += " AND id_collection=%s" params.append(colID) if ln: sql += " AND ln=%s" params.append(ln) if position: sql += " AND position=%s" params.append(position) sql += " ORDER BY position, ln, score desc" res = run_sql(sql, tuple(params)) return res def get_col_fmt(colID=-1): """Returns all formats currently associated with a collection, or for one specific collection colID - the id of the collection""" if colID not in [-1, "-1"]: res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id AND id_collection=%s ORDER BY score desc", (colID, )) else: res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id ORDER BY score desc") return res def get_col_rnk(colID, ln): """ Returns a list of the rank methods the given collection is attached to colID - id from collection""" try: res1 = dict(run_sql("""SELECT "id_rnkMETHOD", '' FROM "collection_rnkMETHOD" WHERE id_collection=%s""", (colID, ))) res2 = get_def_name('', "rnkMETHOD") result = filter(lambda x: x[0] in res1, res2) return result except StandardError as e: return () def get_pbx(): """Returns all portalboxes""" res = run_sql("SELECT id, title, body FROM portalbox ORDER by title,body") return res def get_fld_value(fldvID = ''): """Returns fieldvalue""" sql = "SELECT id, name, value FROM fieldvalue" params = [] if fldvID: sql += " WHERE id=%s" params.append(fldvID) sql += " ORDER BY name" res = run_sql(sql, tuple(params)) return res def get_pbx_pos(): """Returns a list of all the positions for a portalbox""" position = {} position["rt"] = "Right Top" position["lt"] = "Left Top" position["te"] = "Title Epilog" position["tp"] = "Title Prolog" position["ne"] = "Narrow by coll epilog" position["np"] = "Narrow by coll prolog" return position def get_sort_nametypes(): """Return a list of the various translationnames for the fields""" type = {} type['soo'] = 'Sort options' type['seo'] = 'Search options' type['sew'] = 'Search within' return type def get_fmt_nametypes(): """Return a list of the various translationnames for the output formats""" type = [] type.append(('ln', 'Long name')) return type def get_fld_nametypes(): """Return a list of the various translationnames for the fields""" type = [] type.append(('ln', 'Long name')) return type def get_col_nametypes(): """Return a list of the various translationnames for the collections""" type = [] type.append(('ln', 'Collection name')) return type def find_last(tree, start_son): """Find the previous collection in the tree with the same father as start_son""" id_dad = tree[start_son][3] while start_son > 0: start_son -= 1 if tree[start_son][3] == id_dad: return start_son def find_next(tree, start_son): """Find the next collection in the tree with the same father as start_son""" id_dad = tree[start_son][3] while start_son < len(tree): start_son += 1 if tree[start_son][3] == id_dad: return start_son def remove_col_subcol(id_son, id_dad, type): """Remove a collection as a son of another collection in the tree, if collection isn't used elsewhere in the tree, remove all registered sons of the id_son. id_son - collection id of son to remove id_dad - the id of the dad""" try: if id_son != id_dad: tree = get_col_tree(id_son) run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (id_son, id_dad)) else: tree = get_col_tree(id_son, type) run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s and type=%s", (id_son, id_dad, type)) if not run_sql("SELECT id_dad,id_son,type,score from collection_collection WHERE id_son=%s and type=%s", (id_son, type)): for (id, up, down, dad, rtype) in tree: run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (id, dad)) return (1, "") except StandardError as e: return (0, e) def check_col(add_dad, add_son): """Check if the collection can be placed as a son of the dad without causing loops. add_dad - collection id add_son - collection id""" try: stack = [add_dad] res = run_sql("SELECT id_dad FROM collection_collection WHERE id_dad=%s AND id_son=%s", (add_dad, add_son)) if res: raise StandardError while len(stack) > 0: colID = stack.pop() res = run_sql("SELECT id_dad FROM collection_collection WHERE id_son=%s", (colID, )) for id in res: if int(id[0]) == int(add_son): # raise StandardError # this was the original but it didnt work return(0) else: stack.append(id[0]) return (1, "") except StandardError as e: return (0, e) def attach_rnk_col(colID, rnkID): """attach rank method to collection rnkID - id from rnkMETHOD table colID - id of collection, as in collection table """ try: res = run_sql("""INSERT INTO "collection_rnkMETHOD"(id_collection, "id_rnkMETHOD") values (%s,%s)""", (colID, rnkID)) return (1, "") except StandardError as e: register_exception() return (0, e) def detach_rnk_col(colID, rnkID): """detach rank method from collection rnkID - id from rnkMETHOD table colID - id of collection, as in collection table """ try: res = run_sql("""DELETE FROM "collection_rnkMETHOD" WHERE id_collection=%s AND "id_rnkMETHOD"=%s""", (colID, rnkID)) return (1, "") except StandardError as e: register_exception() return (0, e) def switch_col_treescore(col_1, col_2): try: res1 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s", (col_1[3], col_1[0])) res2 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s", (col_2[3], col_2[0])) res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s", (res2[0][0], col_1[3], col_1[0])) res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s", (res1[0][0], col_2[3], col_2[0])) return (1, "") except StandardError as e: register_exception() return (0, e) def move_col_tree(col_from, col_to, move_to_rtype=''): """Move a collection from one point in the tree to another. becomes a son of the endpoint. col_from - move this collection from current point col_to - and set it as a son of this collection. move_to_rtype - either virtual or regular collection""" try: res = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s ORDER BY score asc", (col_to[0], )) highscore = 0 for score in res: if int(score[0]) > highscore: highscore = int(score[0]) highscore += 1 if not move_to_rtype: move_to_rtype = col_from[4] res = run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (col_from[0], col_from[3])) res = run_sql("INSERT INTO collection_collection(id_dad,id_son,score,type) values(%s,%s,%s,%s)", (col_to[0], col_from[0], highscore, move_to_rtype)) return (1, "") except StandardError as e: register_exception() return (0, e) def remove_pbx(colID, pbxID, ln): """Removes a portalbox from the collection given. colID - the collection the format is connected to pbxID - the portalbox which should be removed from the collection. ln - the language of the portalbox to be removed""" try: res = run_sql("DELETE FROM collection_portalbox WHERE id_collection=%s AND id_portalbox=%s AND ln=%s", (colID, pbxID, ln)) return (1, "") except StandardError as e: register_exception() return (0, e) def remove_fmt(colID, fmtID): """Removes a format from the collection given. colID - the collection the format is connected to fmtID - the format which should be removed from the collection.""" try: res = run_sql("DELETE FROM collection_format WHERE id_collection=%s AND id_format=%s", (colID, fmtID)) return (1, "") except StandardError as e: register_exception() return (0, e) def remove_fld(colID, fldID, fldvID=''): """Removes a field from the collection given. colID - the collection the format is connected to fldID - the field which should be removed from the collection.""" try: sql = "DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s" params = [colID, fldID] if fldvID: if fldvID != "None": sql += " AND id_fieldvalue=%s" params.append(fldvID) else: sql += " AND id_fieldvalue is NULL" res = run_sql(sql, tuple(params)) return (1, "") except StandardError as e: register_exception() return (0, e) def delete_fldv(fldvID): """Deletes all data for the given fieldvalue fldvID - delete all data in the tables associated with fieldvalue and this id""" try: res = run_sql("DELETE FROM collection_field_fieldvalue WHERE id_fieldvalue=%s", (fldvID, )) res = run_sql("DELETE FROM fieldvalue WHERE id=%s", (fldvID, )) return (1, "") except StandardError as e: register_exception() return (0, e) def delete_pbx(pbxID): """Deletes all data for the given portalbox pbxID - delete all data in the tables associated with portalbox and this id """ try: res = run_sql("DELETE FROM collection_portalbox WHERE id_portalbox=%s", (pbxID, )) res = run_sql("DELETE FROM portalbox WHERE id=%s", (pbxID, )) return (1, "") except StandardError as e: register_exception() return (0, e) def delete_fmt(fmtID): """Deletes all data for the given format fmtID - delete all data in the tables associated with format and this id """ try: res = run_sql("DELETE FROM format WHERE id=%s", (fmtID, )) res = run_sql("DELETE FROM collection_format WHERE id_format=%s", (fmtID, )) res = run_sql("DELETE FROM formatname WHERE id_format=%s", (fmtID, )) return (1, "") except StandardError as e: register_exception() return (0, e) def delete_col(colID): """Deletes all data for the given collection colID - delete all data in the tables associated with collection and this id """ try: res = run_sql("DELETE FROM collection WHERE id=%s", (colID, )) res = run_sql("DELETE FROM collectionname WHERE id_collection=%s", (colID, )) res = run_sql("""DELETE FROM "collection_rnkMETHOD" WHERE id_collection=%s""", (colID, )) res = run_sql("DELETE FROM collection_collection WHERE id_dad=%s", (colID, )) res = run_sql("DELETE FROM collection_collection WHERE id_son=%s", (colID, )) res = run_sql("DELETE FROM collection_portalbox WHERE id_collection=%s", (colID, )) res = run_sql("DELETE FROM collection_format WHERE id_collection=%s", (colID, )) res = run_sql("DELETE FROM collection_field_fieldvalue WHERE id_collection=%s", (colID, )) return (1, "") except StandardError as e: register_exception() return (0, e) def add_fmt(code, name, rtype): """Add a new output format. Returns the id of the format. code - the code for the format, max 6 chars. name - the default name for the default language of the format. rtype - the default nametype""" try: res = run_sql("INSERT INTO format (code, name) values (%s,%s)", (code, name)) fmtID = run_sql("SELECT id FROM format WHERE code=%s", (code,)) res = run_sql("INSERT INTO formatname(id_format, type, ln, value) VALUES (%s,%s,%s,%s)", (fmtID[0][0], rtype, CFG_SITE_LANG, name)) return (1, fmtID) except StandardError as e: register_exception() return (0, e) def update_fldv(fldvID, name, value): """Modify existing fieldvalue fldvID - id of fieldvalue to modify value - the value of the fieldvalue name - the name of the fieldvalue.""" try: res = run_sql("UPDATE fieldvalue set name=%s where id=%s", (name, fldvID)) res = run_sql("UPDATE fieldvalue set value=%s where id=%s", (value, fldvID)) return (1, "") except StandardError as e: register_exception() return (0, e) def add_fldv(name, value): """Add a new fieldvalue, returns id of fieldvalue value - the value of the fieldvalue name - the name of the fieldvalue.""" try: res = run_sql("SELECT id FROM fieldvalue WHERE name=%s and value=%s", (name, value)) if not res: res = run_sql("INSERT INTO fieldvalue (name, value) values (%s,%s)", (name, value)) res = run_sql("SELECT id FROM fieldvalue WHERE name=%s and value=%s", (name, value)) if res: return (1, res[0][0]) else: raise StandardError except StandardError as e: register_exception() return (0, e) def add_pbx(title, body): try: res = run_sql("INSERT INTO portalbox (title, body) values (%s,%s)", (title, body)) res = run_sql("SELECT id FROM portalbox WHERE title=%s AND body=%s", (title, body)) if res: return (1, res[0][0]) else: raise StandardError except StandardError as e: register_exception() return (0, e) def add_col(colNAME, dbquery=None): """Adds a new collection to collection table colNAME - the default name for the collection, saved to collection and collectionname dbquery - query related to the collection""" # BTW, sometimes '' are passed instead of None, so change them to None if not dbquery: dbquery = None try: rtype = get_col_nametypes()[0][0] colID = run_sql("SELECT id FROM collection WHERE id=1") if colID: res = run_sql("INSERT INTO collection (name,dbquery) VALUES (%s,%s)", (colNAME,dbquery)) else: res = run_sql("INSERT INTO collection (id,name,dbquery) VALUES (1,%s,%s)", (colNAME,dbquery)) colID = run_sql("SELECT id FROM collection WHERE name=%s", (colNAME,)) res = run_sql("INSERT INTO collectionname(id_collection, type, ln, value) VALUES (%s,%s,%s,%s)", (colID[0][0], rtype, CFG_SITE_LANG, colNAME)) if colID: return (1, colID[0][0]) else: raise StandardError except StandardError as e: register_exception() return (0, e) def add_col_pbx(colID, pbxID, ln, position, score=''): """add a portalbox to the collection. colID - the id of the collection involved pbxID - the portalbox to add ln - which language the portalbox is for score - decides which portalbox is the most important position - position on page the portalbox should appear.""" try: if score: res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,'%s',%s,%s)", (pbxID, colID, ln, score, position)) else: res = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and ln=%s and position=%s ORDER BY score desc, ln, position", (colID, ln, position)) if res: score = int(res[0][0]) else: score = 0 res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,%s,%s,%s)", (pbxID, colID, ln, (score + 1), position)) return (1, "") except StandardError as e: register_exception() return (0, e) def add_col_fmt(colID, fmtID, score=''): """Add a output format to the collection. colID - the id of the collection involved fmtID - the id of the format. score - the score of the format, decides sorting, if not given, place the format on top""" try: if score: res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)", (fmtID, colID, score)) else: res = run_sql("SELECT score FROM collection_format WHERE id_collection=%s ORDER BY score desc", (colID, )) if res: score = int(res[0][0]) else: score = 0 res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)", (fmtID, colID, (score + 1))) return (1, "") except StandardError as e: register_exception() return (0, e) def add_col_fld(colID, fldID, type, fldvID=''): """Add a sort/search/field to the collection. colID - the id of the collection involved fldID - the id of the field. fldvID - the id of the fieldvalue. type - which type, seo, sew... score - the score of the format, decides sorting, if not given, place the format on top""" try: if fldvID and fldvID not in [-1, "-1"]: run_sql("DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s and id_fieldvalue is NULL", (colID, fldID, type)) res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s ORDER BY score desc", (colID, fldID, type)) if res: score = int(res[0][0]) res = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s ORDER BY score_fieldvalue desc", (colID, fldID, type)) else: res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and type=%s ORDER BY score desc", (colID, type)) if res: score = int(res[0][0]) + 1 else: score = 1 res = run_sql("SELECT id_collection,id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue where id_field=%s and id_collection=%s and type=%s and id_fieldvalue=%s", (fldID, colID, type, fldvID)) if not res: run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=score_fieldvalue+1 WHERE id_field=%s AND id_collection=%s and type=%s", (fldID, colID, type)) res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_fieldvalue, id_collection, type, score, score_fieldvalue) values (%s,%s,%s,%s,%s,%s)", (fldID, fldvID, colID, type, score, 1)) else: return (0, (1, "Already exists")) else: res = run_sql("SELECT id_collection,id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s AND type=%s and id_field=%s and id_fieldvalue is NULL", (colID, type, fldID)) if res: return (0, (1, "Already exists")) else: run_sql("UPDATE collection_field_fieldvalue SET score=score+1") res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_collection, type, score,score_fieldvalue) values (%s,%s,%s,%s, 0)", (fldID, colID, type, 1)) return (1, "") except StandardError as e: register_exception() return (0, e) def modify_dbquery(colID, dbquery=None): """Modify the dbquery of an collection. colID - the id of the collection involved dbquery - the new dbquery""" # BTW, sometimes '' is passed instead of None, so change it to None if not dbquery: dbquery = None try: res = run_sql("UPDATE collection SET dbquery=%s WHERE id=%s", (dbquery, colID)) return (1, "") except StandardError as e: register_exception() return (0, e) def modify_pbx(colID, pbxID, sel_ln, score='', position='', title='', body=''): """Modify a portalbox colID - the id of the collection involved pbxID - the id of the portalbox that should be modified sel_ln - the language of the portalbox that should be modified title - the title body - the content score - if several portalboxes in one position, who should appear on top. position - position on page""" try: if title: res = run_sql("UPDATE portalbox SET title=%s WHERE id=%s", (title, pbxID)) if body: res = run_sql("UPDATE portalbox SET body=%s WHERE id=%s", (body, pbxID)) if score: res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (score, colID, pbxID, sel_ln)) if position: res = run_sql("UPDATE collection_portalbox SET position=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (position, colID, pbxID, sel_ln)) return (1, "") except Exception as e: register_exception() return (0, e) def switch_fld_score(colID, id_1, id_2): """Switch the scores of id_1 and id_2 in collection_field_fieldvalue colID - collection the id_1 or id_2 is connected to id_1/id_2 - id field from tables like format..portalbox... table - name of the table""" try: res1 = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s", (colID, id_1)) res2 = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s", (colID, id_2)) if res1[0][0] == res2[0][0]: return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem.")) else: res = run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s", (res2[0][0], colID, id_1)) res = run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s", (res1[0][0], colID, id_2)) return (1, "") except StandardError as e: register_exception() return (0, e) def switch_fld_value_score(colID, id_1, fldvID_1, fldvID_2): """Switch the scores of two field_value colID - collection the id_1 or id_2 is connected to id_1/id_2 - id field from tables like format..portalbox... table - name of the table""" try: res1 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (colID, id_1, fldvID_1)) res2 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (colID, id_1, fldvID_2)) if res1[0][0] == res2[0][0]: return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem.")) else: res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (res2[0][0], colID, id_1, fldvID_1)) res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (res1[0][0], colID, id_1, fldvID_2)) return (1, "") except Exception as e: register_exception() return (0, e) def switch_pbx_score(colID, id_1, id_2, sel_ln): """Switch the scores of id_1 and id_2 in the table given by the argument. colID - collection the id_1 or id_2 is connected to id_1/id_2 - id field from tables like format..portalbox... table - name of the table""" try: res1 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln=%s", (colID, id_1, sel_ln)) res2 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln=%s", (colID, id_2, sel_ln)) if res1[0][0] == res2[0][0]: return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem.")) res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (res2[0][0], colID, id_1, sel_ln)) res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (res1[0][0], colID, id_2, sel_ln)) return (1, "") except Exception as e: register_exception() return (0, e) def switch_score(colID, id_1, id_2, table): """Switch the scores of id_1 and id_2 in the table given by the argument. colID - collection the id_1 or id_2 is connected to id_1/id_2 - id field from tables like format..portalbox... table - name of the table""" try: res1 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (colID, id_1)) res2 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (colID, id_2)) if res1[0][0] == res2[0][0]: return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem.")) res = run_sql("UPDATE collection_%s SET score=%%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (res2[0][0], colID, id_1)) res = run_sql("UPDATE collection_%s SET score=%%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (res1[0][0], colID, id_2)) return (1, "") except Exception as e: register_exception() return (0, e) def get_detailed_page_tabs(colID=None, recID=None, ln=CFG_SITE_LANG): """ Returns the complete list of tabs to be displayed in the detailed record pages. Returned structured is a dict with - key : last component of the url that leads to detailed record tab: http:www.../CFG_SITE_RECORD/74/key - values: a dictionary with the following keys: - label: *string* label to be printed as tab (Not localized here) - visible: *boolean* if False, tab should not be shown - enabled: *boolean* if True, tab should be disabled - order: *int* position of the tab in the list of tabs - ln: language of the tab labels returns dict """ _ = gettext_set_language(ln) tabs = {'metadata' : {'label': _('Information'), 'visible': False, 'enabled': True, 'order': 1}, 'references': {'label': _('References'), 'visible': False, 'enabled': True, 'order': 2}, 'citations' : {'label': _('Citations'), 'visible': False, 'enabled': True, 'order': 3}, 'keywords' : {'label': _('Keywords'), 'visible': False, 'enabled': True, 'order': 4}, 'comments' : {'label': _('Comments'), 'visible': False, 'enabled': True, 'order': 5}, 'reviews' : {'label': _('Reviews'), 'visible': False, 'enabled': True, 'order': 6}, 'usage' : {'label': _('Usage statistics'), 'visible': False, 'enabled': True, 'order': 7}, 'files' : {'label': _('Files'), 'visible': False, 'enabled': True, 'order': 8}, 'plots' : {'label': _('Plots'), 'visible': False, 'enabled': True, 'order': 9}, 'holdings' : {'label': _('Holdings'), 'visible': False, 'enabled': True, 'order': 10}, } res = run_sql("SELECT tabs FROM collectiondetailedrecordpagetabs " + \ "WHERE id_collection=%s", (colID, )) if len(res) > 0: tabs_state = res[0][0].split(';') for tab_state in tabs_state: if tab_state in tabs: tabs[tab_state]['visible'] = True; else: # no preference set for this collection. # assume all tabs are displayed for key in tabs.keys(): tabs[key]['visible'] = True if not CFG_WEBCOMMENT_ALLOW_COMMENTS: tabs['comments']['visible'] = False tabs['comments']['enabled'] = False if not CFG_WEBCOMMENT_ALLOW_REVIEWS: tabs['reviews']['visible'] = False tabs['reviews']['enabled'] = False if recID is not None: # Disable references if no references found #bfo = BibFormatObject(recID) #if bfe_references.format_element(bfo, '', '') == '': # tabs['references']['enabled'] = False ## FIXME: the above was commented out because bfe_references ## may be too slow. And we do not really need this anyway ## because we can disable tabs in WebSearch Admin on a ## collection-by-collection basis. If we need this, then we ## should probably call bfo.fields('999') here that should be ## much faster than calling bfe_references. # Disable citations if not citations found #if len(get_cited_by(recID)) == 0: # tabs['citations']['enabled'] = False ## FIXME: the above was commented out because get_cited_by() ## may be too slow. And we do not really need this anyway ## because we can disable tags in WebSearch Admin on a ## collection-by-collection basis. # Disable Files tab if no file found except for Plots: disable_files_tab_p = True for abibdoc in BibRecDocs(recID).list_bibdocs(): abibdoc_type = abibdoc.get_type() if abibdoc_type == 'Plot': continue # ignore attached plots else: if CFG_INSPIRE_SITE and not \ abibdoc_type in ('', 'INSPIRE-PUBLIC', 'Supplementary Material'): # ignore non-empty, non-INSPIRE-PUBLIC, non-suppl doctypes for INSPIRE continue # okay, we found at least one non-Plot file: disable_files_tab_p = False break if disable_files_tab_p: tabs['files']['enabled'] = False # Disable Plots tab if no docfile of doctype Plot found brd = BibRecDocs(recID) if len(brd.list_bibdocs('Plot')) == 0: tabs['plots']['enabled'] = False if CFG_CERN_SITE: from invenio.legacy.search_engine import get_collection_reclist if recID in get_collection_reclist("Books & Proceedings"): tabs['holdings']['visible'] = True tabs['holdings']['enabled'] = True tabs[''] = tabs['metadata'] del tabs['metadata'] return tabs def get_detailed_page_tabs_counts(recID): """ Returns the number of citations, references and comments/reviews that have to be shown on the corresponding tabs in the detailed record pages @param recID: record id @return: dictionary with following keys 'Citations': number of citations to be shown in the "Citations" tab 'References': number of references to be shown in the "References" tab 'Comments': number of comments to be shown in the "Comments" tab 'Reviews': number of reviews to be shown in the "Reviews" tab """ num_comments = 0 #num of comments num_reviews = 0 #num of reviews tabs_counts = {'Citations' : 0, 'References' : -1, 'Discussions' : 0, 'Comments' : 0, 'Reviews' : 0 } from invenio.legacy.search_engine import get_field_tags, get_record if CFG_BIBRANK_SHOW_CITATION_LINKS: if CFG_INSPIRE_SITE: from invenio.legacy.search_engine import search_unit citers_recids = intbitset(get_cited_by(recID)) citeable_recids = search_unit(p='citeable', f='collection') tabs_counts['Citations'] = len(citers_recids & citeable_recids) else: tabs_counts['Citations'] = get_cited_by_count(recID) if not CFG_CERN_SITE:#FIXME:should be replaced by something like CFG_SHOW_REFERENCES reftag = "" reftags = get_field_tags("reference") if reftags: reftag = reftags[0] tmprec = get_record(recID) if reftag and len(reftag) > 4: tabs_counts['References'] = len(record_get_field_instances(tmprec, reftag[0:3], reftag[3], reftag[4])) # obtain number of comments/reviews - from invenio.legacy.webcomment.adminlib import get_nb_reviews, get_nb_comments + from invenio.modules.comments.models import CmtRECORDCOMMENT if CFG_WEBCOMMENT_ALLOW_COMMENTS and CFG_WEBSEARCH_SHOW_COMMENT_COUNT: - num_comments = get_nb_comments(recID, count_deleted=False) + num_comments = CmtRECORDCOMMENT.count(*[ + CmtRECORDCOMMENT.id_bibrec == recID, + CmtRECORDCOMMENT.star_score == 0, + CmtRECORDCOMMENT.status.notin_(['dm', 'da']) + ]) if CFG_WEBCOMMENT_ALLOW_REVIEWS and CFG_WEBSEARCH_SHOW_REVIEW_COUNT: - num_reviews = get_nb_reviews(recID, count_deleted=False) + num_reviews = CmtRECORDCOMMENT.count(*[ + CmtRECORDCOMMENT.id_bibrec == recID, + CmtRECORDCOMMENT.star_score > 0, + CmtRECORDCOMMENT.status.notin_(['dm', 'da']) + ]) if num_comments: tabs_counts['Comments'] = num_comments tabs_counts['Discussions'] += num_comments if num_reviews: tabs_counts['Reviews'] = num_reviews tabs_counts['Discussions'] += num_reviews return tabs_counts diff --git a/invenio/modules/comments/api.py b/invenio/modules/comments/api.py index f73cfe994..67fddebba 100644 --- a/invenio/modules/comments/api.py +++ b/invenio/modules/comments/api.py @@ -1,2195 +1,2207 @@ # -*- coding: utf-8 -*- # This file is part of Invenio. # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2015 CERN. # # Invenio is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # Invenio is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Invenio; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """ Comments and reviews for records """ __revision__ = "$Id$" # non Invenio imports: import time import math import os import shutil import cgi import re from datetime import datetime, timedelta from six import iteritems # Invenio imports: from invenio.legacy.dbquery import run_sql, datetime_format from invenio.config import CFG_COMMENTSDIR, \ CFG_SITE_LANG, \ CFG_WEBALERT_ALERT_ENGINE_EMAIL,\ CFG_SITE_SUPPORT_EMAIL,\ CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL,\ CFG_SITE_URL,\ CFG_SITE_NAME,\ CFG_WEBCOMMENT_ALLOW_REVIEWS,\ CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS,\ CFG_WEBCOMMENT_ALLOW_COMMENTS,\ CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL,\ CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN,\ CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS,\ CFG_WEBCOMMENT_DEFAULT_MODERATOR, \ CFG_SITE_RECORD, \ CFG_WEBCOMMENT_EMAIL_REPLIES_TO, \ CFG_WEBCOMMENT_ROUND_DATAFIELD, \ CFG_WEBCOMMENT_RESTRICTION_DATAFIELD, \ CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH from invenio.utils.mail import \ email_quote_txt, \ email_quoted_txt2html from invenio.utils.html import tidy_html from invenio.legacy.webuser import get_user_info, get_email, collect_user_info from invenio.utils.date import convert_datetext_to_dategui, \ datetext_default, \ convert_datestruct_to_datetext from invenio.ext.email import send_email from invenio.ext.logging import register_exception from invenio.base.i18n import wash_language, gettext_set_language from invenio.utils.url import wash_url_argument from .config import CFG_WEBCOMMENT_ACTION_CODE, \ InvenioWebCommentError, \ InvenioWebCommentWarning from invenio.modules.access.engine import acc_authorize_action +from invenio.modules.access.models import CmtRECORDCOMMENT from invenio.legacy.search_engine import \ guess_primary_collection_of_a_record, \ check_user_can_view_record from invenio.modules.collections.cache import get_collection_reclist from invenio.legacy.bibrecord import get_fieldvalues from invenio.utils.htmlwasher import EmailWasher try: import invenio.legacy.template webcomment_templates = invenio.legacy.template.load('webcomment') except: pass def perform_request_display_comments_or_remarks(req, recID, display_order='od', display_since='all', nb_per_page=100, page=1, ln=CFG_SITE_LANG, voted=-1, reported=-1, subscribed=0, reviews=0, uid=-1, can_send_comments=False, can_attach_files=False, user_is_subscribed_to_discussion=False, user_can_unsubscribe_from_discussion=False, display_comment_rounds=None): """ Returns all the comments (reviews) of a specific internal record or external basket record. @param recID: record id where (internal record IDs > 0) or (external basket record IDs < -100) @param display_order: hh = highest helpful score, review only lh = lowest helpful score, review only hs = highest star score, review only ls = lowest star score, review only od = oldest date nd = newest date @param display_since: all= no filtering by date nd = n days ago nw = n weeks ago nm = n months ago ny = n years ago where n is a single digit integer between 0 and 9 @param nb_per_page: number of results per page @param page: results page @param voted: boolean, active if user voted for a review, see perform_request_vote function @param reported: boolean, active if user reported a certain comment/review, perform_request_report function @param subscribed: int, 1 if user just subscribed to discussion, -1 if unsubscribed @param reviews: boolean, enabled if reviews, disabled for comments @param uid: the id of the user who is reading comments @param can_send_comments: if user can send comment or not @param can_attach_files: if user can attach file to comment or not @param user_is_subscribed_to_discussion: True if user already receives new comments by email @param user_can_unsubscribe_from_discussion: True is user is allowed to unsubscribe from discussion @return html body. """ _ = gettext_set_language(ln) warnings = [] nb_reviews = 0 nb_comments = 0 # wash arguments recID = wash_url_argument(recID, 'int') ln = wash_language(ln) display_order = wash_url_argument(display_order, 'str') display_since = wash_url_argument(display_since, 'str') nb_per_page = wash_url_argument(nb_per_page, 'int') page = wash_url_argument(page, 'int') voted = wash_url_argument(voted, 'int') reported = wash_url_argument(reported, 'int') reviews = wash_url_argument(reviews, 'int') # vital argument check (valid, error_body) = check_recID_is_in_range(recID, warnings, ln) if not(valid): return error_body # CERN hack begins: filter out ATLAS comments from invenio.config import CFG_CERN_SITE if CFG_CERN_SITE: restricted_comments_p = False for report_number in get_fieldvalues(recID, '088__a'): if report_number.startswith("ATL-"): restricted_comments_p = True break if restricted_comments_p: err_code, err_msg = acc_authorize_action(uid, 'viewrestrcoll', collection='ATLAS Communications') if err_code: return err_msg # CERN hack ends # Query the database and filter results user_info = collect_user_info(uid) res = query_retrieve_comments_or_remarks(recID, display_order, display_since, reviews, user_info=user_info) # res2 = query_retrieve_comments_or_remarks(recID, display_order, display_since, not reviews, user_info=user_info) nb_res = len(res) - from invenio.legacy.webcomment.adminlib import get_nb_reviews, get_nb_comments + filters = [ + CmtRECORDCOMMENT.id_bibrec == recID, + CmtRECORDCOMMENT.status.notin_(['dm', 'da']) + ] - nb_reviews = get_nb_reviews(recID, count_deleted=False) - nb_comments = get_nb_comments(recID, count_deleted=False) + nb_reviews = CmtRECORDCOMMENT.count(*( + filters + [ + CmtRECORDCOMMENT.star_score > 0 + ] + )) + + nb_comments = CmtRECORDCOMMENT.count(*( + filters + [ + CmtRECORDCOMMENT.star_score == 0 + ] + )) # checking non vital arguemnts - will be set to default if wrong #if page <= 0 or page.lower() != 'all': if page < 0: page = 1 try: raise InvenioWebCommentWarning(_('Bad page number --> showing first page.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_INVALID_PAGE_NB',)) if nb_per_page < 0: nb_per_page = 100 try: raise InvenioWebCommentWarning(_('Bad number of results per page --> showing 10 results per page.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_INVALID_NB_RESULTS_PER_PAGE',)) if CFG_WEBCOMMENT_ALLOW_REVIEWS and reviews: if display_order not in ['od', 'nd', 'hh', 'lh', 'hs', 'ls']: display_order = 'hh' try: raise InvenioWebCommentWarning(_('Bad display order --> showing most helpful first.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_INVALID_REVIEW_DISPLAY_ORDER',)) else: if display_order not in ['od', 'nd']: display_order = 'od' try: raise InvenioWebCommentWarning(_('Bad display order --> showing oldest first.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_INVALID_DISPLAY_ORDER',)) if not display_comment_rounds: display_comment_rounds = [] # filter results according to page and number of reults per page if nb_per_page > 0: if nb_res > 0: last_page = int(math.ceil(nb_res / float(nb_per_page))) else: last_page = 1 if page > last_page: page = 1 try: raise InvenioWebCommentWarning(_('Bad page number --> showing first page.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(("WRN_WEBCOMMENT_INVALID_PAGE_NB",)) if nb_res > nb_per_page: # if more than one page of results if page < last_page: res = res[(page-1)*(nb_per_page) : (page*nb_per_page)] else: res = res[(page-1)*(nb_per_page) : ] else: # one page of results pass else: last_page = 1 # Add information regarding visibility of comment for user user_collapsed_comments = get_user_collapsed_comments_for_record(uid, recID) if reviews: res = [row[:] + (row[10] in user_collapsed_comments,) for row in res] else: res = [row[:] + (row[6] in user_collapsed_comments,) for row in res] # Send to template avg_score = 0.0 if not CFG_WEBCOMMENT_ALLOW_COMMENTS and not CFG_WEBCOMMENT_ALLOW_REVIEWS: # comments not allowed by admin try: raise InvenioWebCommentError(_('Comments on records have been disallowed by the administrator.')) except InvenioWebCommentError as exc: register_exception(req=req) body = webcomment_templates.tmpl_error(exc.message, ln) return body # errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',)) if reported > 0: try: raise InvenioWebCommentWarning(_('Your feedback has been recorded, many thanks.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, 'green')) #warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED',)) elif reported == 0: try: raise InvenioWebCommentWarning(_('You have already reported an abuse for this comment.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_ALREADY_REPORTED',)) elif reported == -2: try: raise InvenioWebCommentWarning(_('The comment you have reported no longer exists.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_INVALID_REPORT',)) if CFG_WEBCOMMENT_ALLOW_REVIEWS and reviews: avg_score = calculate_avg_score(res) if voted > 0: try: raise InvenioWebCommentWarning(_('Your feedback has been recorded, many thanks.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, 'green')) #warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED',)) elif voted == 0: try: raise InvenioWebCommentWarning(_('Sorry, you have already voted. This vote has not been recorded.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_ALREADY_VOTED',)) if subscribed == 1: try: raise InvenioWebCommentWarning(_('You have been subscribed to this discussion. From now on, you will receive an email whenever a new comment is posted.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, 'green')) #warnings.append(('WRN_WEBCOMMENT_SUBSCRIBED',)) elif subscribed == -1: try: raise InvenioWebCommentWarning(_('You have been unsubscribed from this discussion.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, 'green')) #warnings.append(('WRN_WEBCOMMENT_UNSUBSCRIBED',)) grouped_comments = group_comments_by_round(res, reviews) # Clean list of comments round names if not display_comment_rounds: display_comment_rounds = [] elif 'all' in display_comment_rounds: display_comment_rounds = [cmtgrp[0] for cmtgrp in grouped_comments] elif 'latest' in display_comment_rounds: if grouped_comments: display_comment_rounds.append(grouped_comments[-1][0]) display_comment_rounds.remove('latest') body = webcomment_templates.tmpl_get_comments(req, recID, ln, nb_per_page, page, last_page, display_order, display_since, CFG_WEBCOMMENT_ALLOW_REVIEWS, grouped_comments, nb_comments, avg_score, warnings, border=0, reviews=reviews, total_nb_reviews=nb_reviews, 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) return body def perform_request_vote(cmt_id, client_ip_address, value, uid=-1): """ Vote positively or negatively for a comment/review @param cmt_id: review id @param value: +1 for voting positively -1 for voting negatively @return: integer 1 if successful, integer 0 if not """ cmt_id = wash_url_argument(cmt_id, 'int') client_ip_address = wash_url_argument(client_ip_address, 'str') value = wash_url_argument(value, 'int') uid = wash_url_argument(uid, 'int') if cmt_id > 0 and value in [-1, 1] and check_user_can_vote(cmt_id, client_ip_address, uid): action_date = convert_datestruct_to_datetext(time.localtime()) action_code = CFG_WEBCOMMENT_ACTION_CODE['VOTE'] # FIXME compatibility with postgresql query = """INSERT INTO "cmtACTIONHISTORY" ("id_cmtRECORDCOMMENT", id_bibrec, id_user, client_host, action_time, action_code) VALUES (%s, NULL ,%s, inet_aton(%s), %s, %s)""" params = (cmt_id, uid, client_ip_address, action_date, action_code) run_sql(query, params) return query_record_useful_review(cmt_id, value) else: return 0 def check_user_can_comment(recID, client_ip_address, uid=-1): """ Check if a user hasn't already commented within the last seconds time limit: CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS @param recID: record id @param client_ip_address: IP => use: str(req.remote_ip) @param uid: user id, as given by invenio.legacy.webuser.getUid(req) """ recID = wash_url_argument(recID, 'int') client_ip_address = wash_url_argument(client_ip_address, 'str') uid = wash_url_argument(uid, 'int') max_action_time = time.time() - CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS max_action_time = convert_datestruct_to_datetext(time.localtime(max_action_time)) action_code = CFG_WEBCOMMENT_ACTION_CODE['ADD_COMMENT'] query = """SELECT id_bibrec FROM "cmtACTIONHISTORY" WHERE id_bibrec=%s AND action_code=%s AND action_time>%s """ params = (recID, action_code, max_action_time) if uid < 0: query += " AND client_host=inet_aton(%s)" params += (client_ip_address,) else: query += " AND id_user=%s" params += (uid,) res = run_sql(query, params) return len(res) == 0 def check_user_can_review(recID, client_ip_address, uid=-1): """ Check if a user hasn't already reviewed within the last seconds time limit: CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS @param recID: record ID @param client_ip_address: IP => use: str(req.remote_ip) @param uid: user id, as given by invenio.legacy.webuser.getUid(req) """ action_code = CFG_WEBCOMMENT_ACTION_CODE['ADD_REVIEW'] query = """SELECT id_bibrec FROM "cmtACTIONHISTORY" WHERE id_bibrec=%s AND action_code=%s """ params = (recID, action_code) if uid < 0: query += " AND client_host=inet_aton(%s)" params += (client_ip_address,) else: query += " AND id_user=%s" params += (uid,) res = run_sql(query, params) return len(res) == 0 def check_user_can_vote(cmt_id, client_ip_address, uid=-1): """ Checks if a user hasn't already voted @param cmt_id: comment id @param client_ip_address: IP => use: str(req.remote_ip) @param uid: user id, as given by invenio.legacy.webuser.getUid(req) """ cmt_id = wash_url_argument(cmt_id, 'int') client_ip_address = wash_url_argument(client_ip_address, 'str') uid = wash_url_argument(uid, 'int') query = """SELECT "id_cmtRECORDCOMMENT" FROM "cmtACTIONHISTORY" WHERE "id_cmtRECORDCOMMENT"=%s""" params = (cmt_id,) if uid < 0: query += " AND client_host=inet_aton(%s)" params += (client_ip_address,) else: query += " AND id_user=%s" params += (uid, ) res = run_sql(query, params) return (len(res) == 0) def get_comment_collection(cmt_id): """ Extract the collection where the comment is written """ query = """SELECT id_bibrec FROM "cmtRECORDCOMMENT" WHERE id=%s""" recid = run_sql(query, (cmt_id,)) record_primary_collection = guess_primary_collection_of_a_record(recid[0][0]) return record_primary_collection def get_collection_moderators(collection): """ Return the list of comment moderators for the given collection. """ from invenio.modules.access.engine import acc_get_authorized_emails res = list(acc_get_authorized_emails('moderatecomments', collection=collection)) if not res: return [CFG_WEBCOMMENT_DEFAULT_MODERATOR,] return res def perform_request_report(cmt_id, client_ip_address, uid=-1): """ Report a comment/review for inappropriate content. Will send an email to the administrator if number of reports is a multiple of CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN @param cmt_id: comment id @return: integer 1 if successful, integer 0 if not. -2 if comment does not exist """ cmt_id = wash_url_argument(cmt_id, 'int') if cmt_id <= 0: return 0 (query_res, nb_abuse_reports) = query_record_report_this(cmt_id) if query_res == 0: return 0 elif query_res == -2: return -2 if not(check_user_can_report(cmt_id, client_ip_address, uid)): return 0 action_date = convert_datestruct_to_datetext(time.localtime()) action_code = CFG_WEBCOMMENT_ACTION_CODE['REPORT_ABUSE'] query = """INSERT INTO "cmtACTIONHISTORY" ("id_cmtRECORDCOMMENT", id_bibrec, id_user, client_host, action_time, action_code) VALUES (%s, NULL, %s, inet_aton(%s), %s, %s)""" params = (cmt_id, uid, client_ip_address, action_date, action_code) run_sql(query, params) if nb_abuse_reports % CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN == 0: (cmt_id2, id_bibrec, id_user, cmt_body, cmt_date, cmt_star, cmt_vote, cmt_nb_votes_total, cmt_title, cmt_reported, round_name, restriction) = query_get_comment(cmt_id) (user_nb_abuse_reports, user_votes, user_nb_votes_total) = query_get_user_reports_and_votes(int(id_user)) (nickname, user_email, last_login) = query_get_user_contact_info(id_user) from_addr = '%s Alert Engine <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL) comment_collection = get_comment_collection(cmt_id) to_addrs = get_collection_moderators(comment_collection) subject = "A comment has been reported as inappropriate by a user" body = ''' The following comment has been reported a total of %(cmt_reported)s times. Author: nickname = %(nickname)s email = %(user_email)s user_id = %(uid)s This user has: total number of reports = %(user_nb_abuse_reports)s %(votes)s Comment: comment_id = %(cmt_id)s record_id = %(id_bibrec)s date written = %(cmt_date)s nb reports = %(cmt_reported)s %(review_stuff)s body = ---start body--- %(cmt_body)s ---end body--- Please go to the record page %(comment_admin_link)s to delete this message if necessary. A warning will be sent to the user in question.''' % \ { 'cfg-report_max' : CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN, 'nickname' : nickname, 'user_email' : user_email, 'uid' : id_user, 'user_nb_abuse_reports' : user_nb_abuse_reports, 'user_votes' : user_votes, 'votes' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \ "total number of positive votes\t= %s\n\t\ttotal number of negative votes\t= %s" % \ (user_votes, (user_nb_votes_total - user_votes)) or "\n", 'cmt_id' : cmt_id, 'id_bibrec' : id_bibrec, 'cmt_date' : cmt_date, 'cmt_reported' : cmt_reported, 'review_stuff' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \ "star score\t= %s\n\treview title\t= %s" % (cmt_star, cmt_title) or "", 'cmt_body' : cmt_body, 'comment_admin_link' : CFG_SITE_URL + "/"+ CFG_SITE_RECORD +"/" + str(id_bibrec) + '/comments#' + str(cmt_id), 'user_admin_link' : "user_admin_link" #! FIXME } #FIXME to be added to email when websession module is over: #If you wish to ban the user, you can do so via the User Admin Panel %(user_admin_link)s. send_email(from_addr, to_addrs, subject, body) return 1 def check_user_can_report(cmt_id, client_ip_address, uid=-1): """ Checks if a user hasn't already reported a comment @param cmt_id: comment id @param client_ip_address: IP => use: str(req.remote_ip) @param uid: user id, as given by invenio.legacy.webuser.getUid(req) """ cmt_id = wash_url_argument(cmt_id, 'int') client_ip_address = wash_url_argument(client_ip_address, 'str') uid = wash_url_argument(uid, 'int') query = """SELECT "id_cmtRECORDCOMMENT" FROM "cmtACTIONHISTORY" WHERE "id_cmtRECORDCOMMENT"=%s""" params = (uid,) if uid < 0: query += " AND client_host=inet_aton(%s)" params += (client_ip_address,) else: query += " AND id_user=%s" params += (uid,) res = run_sql(query, params) return (len(res) == 0) def query_get_user_contact_info(uid): """ Get the user contact information @return: tuple (nickname, email, last_login), if none found return () Note: for the moment, if no nickname, will return email address up to the '@' """ # FIXME compatibility with postgresql query1 = """SELECT nickname, email, """ + \ datetime_format('last_login') + \ """ FROM "user" WHERE id=%s""" params1 = (uid,) res1 = run_sql(query1, params1) if res1: return res1[0] else: return () def query_get_user_reports_and_votes(uid): """ Retrieve total number of reports and votes of a particular user @param uid: user id @return: tuple (total_nb_reports, total_nb_votes_yes, total_nb_votes_total) if none found return () """ query1 = """SELECT nb_votes_yes, nb_votes_total, nb_abuse_reports FROM "cmtRECORDCOMMENT" WHERE id_user=%s""" params1 = (uid,) res1 = run_sql(query1, params1) if len(res1) == 0: return () nb_votes_yes = nb_votes_total = nb_abuse_reports = 0 for cmt_tuple in res1: nb_votes_yes += int(cmt_tuple[0]) nb_votes_total += int(cmt_tuple[1]) nb_abuse_reports += int(cmt_tuple[2]) return (nb_abuse_reports, nb_votes_yes, nb_votes_total) def query_get_comment(comID): """ Get all fields of a comment @param comID: comment id @return: tuple (comID, id_bibrec, id_user, body, date_creation, star_score, nb_votes_yes, nb_votes_total, title, nb_abuse_reports, round_name, restriction) if none found return () """ query1 = """SELECT id, id_bibrec, id_user, body, """ + \ datetime_format('date_creation') + ', ' \ """ star_score, nb_votes_yes, nb_votes_total, title, nb_abuse_reports, round_name, restriction FROM "cmtRECORDCOMMENT" WHERE id=%s""" params1 = (comID,) res1 = run_sql(query1, params1) if len(res1)>0: return res1[0] else: return () def query_record_report_this(comID): """ Increment the number of reports for a comment @param comID: comment id @return: tuple (success, new_total_nb_reports_for_this_comment) where success is integer 1 if success, integer 0 if not, -2 if comment does not exist """ #retrieve nb_abuse_reports query1 = """SELECT nb_abuse_reports FROM "cmtRECORDCOMMENT" WHERE id=%s""" params1 = (comID,) res1 = run_sql(query1, params1) if len(res1) == 0: return (-2, 0) #increment and update nb_abuse_reports = int(res1[0][0]) + 1 query2 = """UPDATE "cmtRECORDCOMMENT" SET nb_abuse_reports=%s WHERE id=%s""" params2 = (nb_abuse_reports, comID) res2 = run_sql(query2, params2) return (int(res2), nb_abuse_reports) def query_record_useful_review(comID, value): """ private funciton Adjust the number of useful votes and number of total votes for a comment. @param comID: comment id @param value: +1 or -1 @return: integer 1 if successful, integer 0 if not """ # retrieve nb_useful votes query1 = """SELECT nb_votes_total, nb_votes_yes FROM "cmtRECORDCOMMENT" WHERE id=%s""" params1 = (comID,) res1 = run_sql(query1, params1) if len(res1)==0: return 0 # modify and insert new nb_useful votes nb_votes_yes = int(res1[0][1]) if value >= 1: nb_votes_yes = int(res1[0][1]) + 1 nb_votes_total = int(res1[0][0]) + 1 query2 = """UPDATE "cmtRECORDCOMMENT" SET nb_votes_total=%s, nb_votes_yes=%s WHERE id=%s""" params2 = (nb_votes_total, nb_votes_yes, comID) res2 = run_sql(query2, params2) return int(res2) def query_retrieve_comments_or_remarks(recID, display_order='od', display_since='0000-00-00 00:00:00', ranking=0, limit='all', user_info=None): """ Private function Retrieve tuple of comments or remarks from the database @param recID: record id @param display_order: hh = highest helpful score lh = lowest helpful score hs = highest star score ls = lowest star score od = oldest date nd = newest date @param display_since: datetime, e.g. 0000-00-00 00:00:00 @param ranking: boolean, enabled if reviews, disabled for comments @param limit: number of comments/review to return @return: tuple of comment where comment is tuple (nickname, uid, date_creation, body, status, id) if ranking disabled or tuple (nickname, uid, date_creation, body, status, nb_votes_yes, nb_votes_total, star_score, title, id) Note: for the moment, if no nickname, will return email address up to '@' """ display_since = calculate_start_date(display_since) order_dict = { 'hh' : "cmt.nb_votes_yes/(cmt.nb_votes_total+1) DESC, cmt.date_creation DESC ", 'lh' : "cmt.nb_votes_yes/(cmt.nb_votes_total+1) ASC, cmt.date_creation ASC ", 'ls' : "cmt.star_score ASC, cmt.date_creation DESC ", 'hs' : "cmt.star_score DESC, cmt.date_creation DESC ", 'nd' : "cmt.reply_order_cached_data DESC ", 'od' : "cmt.reply_order_cached_data ASC " } # Ranking only done for comments and when allowed if ranking and recID > 0: try: display_order = order_dict[display_order] except: display_order = order_dict['od'] else: # in case of recID > 0 => external record => no ranking! ranking = 0 try: if display_order[-1] == 'd': display_order = order_dict[display_order] else: display_order = order_dict['od'] except: display_order = order_dict['od'] #display_order = order_dict['nd'] query = """SELECT user.nickname, cmt.id_user, """ + \ datetime_format('cmt.date_creation', False, True) + ', ' \ """ cmt.body, cmt.status, cmt.nb_abuse_reports, %(ranking)s cmt.id, cmt.round_name, cmt.restriction, %(reply_to_column)s FROM "cmtRECORDCOMMENT" cmt LEFT JOIN "user" ON "user".id=cmt.id_user WHERE cmt.id_bibrec=%%s %(ranking_only)s %(display_since)s ORDER BY %(display_order)s """ % {'ranking' : ranking and ' cmt.nb_votes_yes, cmt.nb_votes_total, cmt.star_score, cmt.title, ' or '', 'ranking_only' : ranking and ' AND cmt.star_score>0 ' or ' AND cmt.star_score=0 ', # 'id_bibrec' : recID > 0 and 'cmt.id_bibrec' or 'cmt.id_bibrec_or_bskEXTREC', # 'table' : recID > 0 and 'cmtRECORDCOMMENT' or 'bskRECORDCOMMENT', 'display_since' : display_since == '0000-00-00 00:00:00' and ' ' or 'AND cmt.date_creation>=\'%s\' ' % display_since, 'display_order': display_order, 'reply_to_column': recID > 0 and 'cmt."in_reply_to_id_cmtRECORDCOMMENT"' or 'cmt."in_reply_to_id_bskRECORDCOMMENT"'} params = (recID,) res = run_sql(query, params) # return res new_limit = limit comments_list = [] for row in res: if ranking: # when dealing with reviews, row[12] holds restriction info: restriction = row[12] else: # when dealing with comments, row[8] holds restriction info: restriction = row[8] if user_info and check_user_can_view_comment(user_info, None, restriction)[0] != 0: # User cannot view comment. Look further continue comments_list.append(row) if limit.isdigit(): new_limit -= 1 if limit < 1: break if comments_list: if limit.isdigit(): return comments_list[:limit] else: return comments_list return () # def get_comment_children(comID): # """ # Returns the list of children (i.e. direct descendants) ordered by time of addition. # @param comID: the ID of the comment for which we want to retrieve children # @type comID: int # @return the list of children # @rtype: list # """ # res = run_sql("SELECT id FROM cmtRECORDCOMMENT WHERE in_reply_to_id_cmtRECORDCOMMENT=%s", (comID,)) # return [row[0] for row in res] # def get_comment_descendants(comID, depth=None): # """ # Returns the list of descendants of the given comment, orderd from # oldest to newest ("top-down"), down to depth specified as parameter. # @param comID: the ID of the comment for which we want to retrieve descendant # @type comID: int # @param depth: the max depth down to which we want to retrieve # descendants. Specify None for no limit, 1 for direct # children only, etc. # @return the list of ancestors # @rtype: list(tuple(comment ID, descendants comments IDs)) # """ # if depth == 0: # return (comID, []) # res = run_sql("SELECT id FROM cmtRECORDCOMMENT WHERE in_reply_to_id_cmtRECORDCOMMENT=%s", (comID,)) # if res: # children_comID = [row[0] for row in res] # children_descendants = [] # if depth: # depth -= 1 # children_descendants = [get_comment_descendants(child_comID, depth) for child_comID in children_comID] # return (comID, children_descendants) # else: # return (comID, []) def get_comment_ancestors(comID, depth=None): """ Returns the list of ancestors of the given comment, ordered from oldest to newest ("top-down": direct parent of comID is at last position), up to given depth @param comID: the ID of the comment for which we want to retrieve ancestors @type comID: int @param depth: the maximum of levels up from the given comment we want to retrieve ancestors. None for no limit, 1 for direct parent only, etc. @type depth: int @return the list of ancestors @rtype: list """ if depth == 0: return [] res = run_sql("""SELECT "in_reply_to_id_cmtRECORDCOMMENT" FROM "cmtRECORDCOMMENT" WHERE id=%s""", (comID,)) if res: parent_comID = res[0][0] if parent_comID == 0: return [] parent_ancestors = [] if depth: depth -= 1 parent_ancestors = get_comment_ancestors(parent_comID, depth) parent_ancestors.append(parent_comID) return parent_ancestors else: return [] def get_reply_order_cache_data(comid): """ Prepare a representation of the comment ID given as parameter so that it is suitable for byte ordering in MySQL. """ return "%s%s%s%s" % (chr((comid >> 24) % 256), chr((comid >> 16) % 256), chr((comid >> 8) % 256), chr(comid % 256)) def query_add_comment_or_remark(reviews=0, recID=0, uid=-1, msg="", note="", score=0, priority=0, client_ip_address='', editor_type='textarea', req=None, reply_to=None, attached_files=None): """ Private function Insert a comment/review or remarkinto the database @param recID: record id @param uid: user id @param msg: comment body @param note: comment title @param score: review star score @param priority: remark priority #!FIXME @param editor_type: the kind of editor used to submit the comment: 'textarea', 'ckeditor' @param req: request object. If provided, email notification are sent after we reply to user request. @param reply_to: the id of the comment we are replying to with this inserted comment. @return: integer >0 representing id if successful, integer 0 if not """ current_date = calculate_start_date('0d') #change utf-8 message into general unicode msg = msg.decode('utf-8') note = note.decode('utf-8') #change general unicode back to utf-8 msg = msg.encode('utf-8') note = note.encode('utf-8') - msg_original = msg (restriction, round_name) = get_record_status(recID) if attached_files is None: attached_files = {} if reply_to and CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH >= 0: # Check that we have not reached max depth comment_ancestors = get_comment_ancestors(reply_to) if len(comment_ancestors) >= CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH: if CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH == 0: reply_to = None else: reply_to = comment_ancestors[CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH - 1] # Inherit restriction and group/round of 'parent' comment = query_get_comment(reply_to) if comment: (round_name, restriction) = comment[10:12] if editor_type == 'ckeditor': # Here we remove the line feeds introduced by CKEditor (they # have no meaning for the user) and replace the HTML line # breaks by linefeeds, so that we are close to an input that # would be done without the CKEditor. That's much better if a # reply to a comment is made with a browser that does not # support CKEditor. msg = msg.replace('\n', '').replace('\r', '') # We clean the quotes that could have been introduced by # CKEditor when clicking the 'quote' button, as well as those # that we have introduced when quoting the original message. # We can however not use directly '>>' chars to quote, as it # will be washed/fixed when calling tidy_html(): double-escape # all > first, and use >> msg = msg.replace('>', '&gt;') msg = re.sub('^\s*<blockquote', '<br/> <blockquote', msg) msg = re.sub('<blockquote.*?>\s*<(p|div).*?>', '>>', msg) msg = re.sub('</(p|div)>\s*</blockquote>', '', msg) # Then definitely remove any blockquote, whatever it is msg = re.sub('<blockquote.*?>', '<div>', msg) msg = re.sub('</blockquote>', '</div>', msg) # Tidy up the HTML msg = tidy_html(msg) # We remove EOL that might have been introduced when tidying msg = msg.replace('\n', '').replace('\r', '') # Now that HTML has been cleaned, unescape > msg = msg.replace('>', '>') msg = msg.replace('&gt;', '>') msg = re.sub('<br .*?(/>)', '\n', msg) msg = msg.replace(' ', ' ') # In case additional <p> or <div> got inserted, interpret # these as new lines (with a sad trick to do it only once) # (note that it has been deactivated, as it is messing up # indentation with >>) #msg = msg.replace('</div><', '</div>\n<') #msg = msg.replace('</p><', '</p>\n<') query = """INSERT INTO "cmtRECORDCOMMENT" (id_bibrec, id_user, body, date_creation, star_score, nb_votes_total, title, round_name, restriction, "in_reply_to_id_cmtRECORDCOMMENT") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" params = (recID, uid, msg, current_date, score, 0, note, round_name, restriction, reply_to or 0) res = run_sql(query, params) if res: new_comid = int(res) move_attached_files_to_storage(attached_files, recID, new_comid) parent_reply_order = run_sql("""SELECT reply_order_cached_data from "cmtRECORDCOMMENT" where id=%s""", (reply_to,)) if not parent_reply_order or parent_reply_order[0][0] is None: # This is not a reply, but a first 0-level comment parent_reply_order = '' else: parent_reply_order = parent_reply_order[0][0] run_sql("""UPDATE "cmtRECORDCOMMENT" SET reply_order_cached_data=%s WHERE id=%s""", (parent_reply_order + get_reply_order_cache_data(new_comid), new_comid)) action_code = CFG_WEBCOMMENT_ACTION_CODE[reviews and 'ADD_REVIEW' or 'ADD_COMMENT'] action_time = convert_datestruct_to_datetext(time.localtime()) query2 = """INSERT INTO "cmtACTIONHISTORY" ("id_cmtRECORDCOMMENT", id_bibrec, id_user, client_host, action_time, action_code) VALUES (%s, %s, %s, inet_aton(%s), %s, %s)""" params2 = (res, recID, uid, client_ip_address, action_time, action_code) run_sql(query2, params2) def notify_subscribers_callback(data): """ Define a callback that retrieves subscribed users, and notify them by email. @param data: contains the necessary parameters in a tuple: (recid, uid, comid, msg, note, score, editor_type, reviews) """ recid, uid, comid, msg, note, score, editor_type, reviews = data # Email this comment to 'subscribers' (subscribers_emails1, subscribers_emails2) = \ get_users_subscribed_to_discussion(recid) email_subscribers_about_new_comment(recid, reviews=reviews, emails1=subscribers_emails1, emails2=subscribers_emails2, comID=comid, msg=msg, note=note, score=score, editor_type=editor_type, uid=uid) # Register our callback to notify subscribed people after # having replied to our current user. data = (recID, uid, res, msg, note, score, editor_type, reviews) if req: req.register_cleanup(notify_subscribers_callback, data) else: notify_subscribers_callback(data) return int(res) def move_attached_files_to_storage(attached_files, recID, comid): """ Move the files that were just attached to a new comment to their final location. @param attached_files: the mappings of desired filename to attach and path where to find the original file @type attached_files: dict {filename, filepath} @param recID: the record ID to which we attach the files @param comid: the comment ID to which we attach the files """ for filename, filepath in iteritems(attached_files): dest_dir = os.path.join(CFG_COMMENTSDIR, str(recID), str(comid)) try: os.makedirs(dest_dir) except: # Dir most probably already existed pass shutil.move(filepath, os.path.join(dest_dir, filename)) def get_attached_files(recid, comid): """ Returns a list with tuples (filename, filepath, fileurl) @param recid: the recid to which the comment belong @param comid: the commment id for which we want to retrieve files """ base_dir = os.path.join(CFG_COMMENTSDIR, str(recid), str(comid)) if os.path.isdir(base_dir): filenames = os.listdir(base_dir) return [(filename, os.path.join(CFG_COMMENTSDIR, str(recid), str(comid), filename), CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recid) + '/comments/attachments/get/' + str(comid) + '/' + filename) \ for filename in filenames] else: return [] def subscribe_user_to_discussion(recID, uid): """ Subscribe a user to a discussion, so the she receives by emails all new new comments for this record. @param recID: record ID corresponding to the discussion we want to subscribe the user @param uid: user id """ query = """INSERT INTO "cmtSUBSCRIPTION" (id_bibrec, id_user, creation_time) VALUES (%s, %s, %s)""" params = (recID, uid, convert_datestruct_to_datetext(time.localtime())) try: run_sql(query, params) except: return 0 return 1 def unsubscribe_user_from_discussion(recID, uid): """ Unsubscribe users from a discussion. @param recID: record ID corresponding to the discussion we want to unsubscribe the user @param uid: user id @return 1 if successful, 0 if not """ query = """DELETE FROM "cmtSUBSCRIPTION" WHERE id_bibrec=%s AND id_user=%s""" params = (recID, uid) try: res = run_sql(query, params) except: return 0 if res > 0: return 1 return 0 def get_user_subscription_to_discussion(recID, uid): """ Returns the type of subscription for the given user to this discussion. This does not check authorizations (for eg. if user was subscribed, but is suddenly no longer authorized). @param recID: record ID @param uid: user id @return: - 0 if user is not subscribed to discussion - 1 if user is subscribed, and is allowed to unsubscribe - 2 if user is subscribed, but cannot unsubscribe """ user_email = get_email(uid) (emails1, emails2) = get_users_subscribed_to_discussion(recID, check_authorizations=False) if user_email in emails1: return 1 elif user_email in emails2: return 2 else: return 0 def get_users_subscribed_to_discussion(recID, check_authorizations=True): """ Returns the lists of users subscribed to a given discussion. Two lists are returned: the first one is the list of emails for users who can unsubscribe from the discussion, the second list contains the emails of users who cannot unsubscribe (for eg. author of the document, etc). Users appear in only one list. If a user has manually subscribed to a discussion AND is an automatic recipients for updates, it will only appear in the second list. @param recID: record ID for which we want to retrieve subscribed users @param check_authorizations: if True, check again if users are authorized to view comment @return tuple (emails1, emails2) """ subscribers_emails = {} # Get users that have subscribed to this discussion query = """SELECT id_user FROM "cmtSUBSCRIPTION" WHERE id_bibrec=%s""" params = (recID,) res = run_sql(query, params) for row in res: uid = row[0] if check_authorizations: user_info = collect_user_info(uid) (auth_code, auth_msg) = check_user_can_view_comments(user_info, recID) else: # Don't check and grant access auth_code = False if auth_code: # User is no longer authorized to view comments. # Delete subscription unsubscribe_user_from_discussion(recID, uid) else: email = get_email(uid) if '@' in email: subscribers_emails[email] = True # Get users automatically subscribed, based on the record metadata collections_with_auto_replies = CFG_WEBCOMMENT_EMAIL_REPLIES_TO.keys() for collection in collections_with_auto_replies: if recID in get_collection_reclist(collection): fields = CFG_WEBCOMMENT_EMAIL_REPLIES_TO[collection] for field in fields: emails = get_fieldvalues(recID, field) for email in emails: if not '@' in email: # Is a group: add domain name subscribers_emails[email + '@' + \ CFG_SITE_SUPPORT_EMAIL.split('@')[1]] = False else: subscribers_emails[email] = False return ([email for email, can_unsubscribe_p \ in iteritems(subscribers_emails) if can_unsubscribe_p], [email for email, can_unsubscribe_p \ in iteritems(subscribers_emails) if not can_unsubscribe_p] ) def email_subscribers_about_new_comment(recID, reviews, emails1, emails2, comID, msg="", note="", score=0, editor_type='textarea', ln=CFG_SITE_LANG, uid=-1): """ Notify subscribers that a new comment was posted. FIXME: consider recipient preference to send email in correct language. @param recID: record id @param emails1: list of emails for users who can unsubscribe from discussion @param emails2: list of emails for users who cannot unsubscribe from discussion @param comID: the comment id @param msg: comment body @param note: comment title @param score: review star score @param editor_type: the kind of editor used to submit the comment: 'textarea', 'ckeditor' @rtype: bool @return: True if email was sent okay, False if it was not. """ _ = gettext_set_language(ln) if not emails1 and not emails2: return 0 # Get title titles = get_fieldvalues(recID, "245__a") if not titles: # usual title not found, try conference title: titles = get_fieldvalues(recID, "111__a") title = '' if titles: title = titles[0] else: title = _("Record %(x_rec)i", x_rec=recID) # Get report number report_numbers = get_fieldvalues(recID, "037__a") if not report_numbers: report_numbers = get_fieldvalues(recID, "088__a") if not report_numbers: report_numbers = get_fieldvalues(recID, "021__a") # Prepare email subject and body if reviews: email_subject = _('%(report_number)s"%(title)s" has been reviewed') % \ {'report_number': report_numbers and ('[' + report_numbers[0] + '] ') or '', 'title': title} else: email_subject = _('%(report_number)s"%(title)s" has been commented') % \ {'report_number': report_numbers and ('[' + report_numbers[0] + '] ') or '', 'title': title} washer = EmailWasher() msg = washer.wash(msg) msg = msg.replace('>>', '>') email_content = msg if note: email_content = note + email_content # Send emails to people who can unsubscribe email_header = webcomment_templates.tmpl_email_new_comment_header(recID, title, reviews, comID, report_numbers, can_unsubscribe=True, ln=ln, uid=uid) email_footer = webcomment_templates.tmpl_email_new_comment_footer(recID, title, reviews, comID, report_numbers, can_unsubscribe=True, ln=ln) res1 = True if emails1: res1 = send_email(fromaddr=CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL, toaddr=emails1, subject=email_subject, content=email_content, header=email_header, footer=email_footer, ln=ln) # Then send email to people who have been automatically # subscribed to the discussion (they cannot unsubscribe) email_header = webcomment_templates.tmpl_email_new_comment_header(recID, title, reviews, comID, report_numbers, can_unsubscribe=False, ln=ln, uid=uid) email_footer = webcomment_templates.tmpl_email_new_comment_footer(recID, title, reviews, comID, report_numbers, can_unsubscribe=False, ln=ln) res2 = True if emails2: res2 = send_email(fromaddr=CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL, toaddr=emails2, subject=email_subject, content=email_content, header=email_header, footer=email_footer, ln=ln) return res1 and res2 def get_record_status(recid): """ Returns the current status of the record, i.e. current restriction to apply for newly submitted comments, and current commenting round. The restriction to apply can be found in the record metadata, in field(s) defined by config CFG_WEBCOMMENT_RESTRICTION_DATAFIELD. The restriction is empty string "" in cases where the restriction has not explicitely been set, even if the record itself is restricted. @param recid: the record id @type recid: int @return tuple(restriction, round_name), where 'restriction' is empty string when no restriction applies @rtype (string, int) """ collections_with_rounds = CFG_WEBCOMMENT_ROUND_DATAFIELD.keys() commenting_round = "" for collection in collections_with_rounds: # Find the first collection defines rounds field for this # record if recid in get_collection_reclist(collection): commenting_rounds = get_fieldvalues(recid, CFG_WEBCOMMENT_ROUND_DATAFIELD.get(collection, "")) if commenting_rounds: commenting_round = commenting_rounds[0] break collections_with_restrictions = CFG_WEBCOMMENT_RESTRICTION_DATAFIELD.keys() restriction = "" for collection in collections_with_restrictions: # Find the first collection that defines restriction field for # this record if recid in get_collection_reclist(collection): restrictions = get_fieldvalues(recid, CFG_WEBCOMMENT_RESTRICTION_DATAFIELD.get(collection, "")) if restrictions: restriction = restrictions[0] break return (restriction, commenting_round) def calculate_start_date(display_since): """ Private function Returns the datetime of display_since argument in MYSQL datetime format calculated according to the local time. @param display_since: = all= no filtering nd = n days ago nw = n weeks ago nm = n months ago ny = n years ago where n is a single digit number @return: string of wanted datetime. If 'all' given as argument, will return datetext_default datetext_default is defined in miscutils/lib/dateutils and equals 0000-00-00 00:00:00 => MySQL format If bad arguement given, will return datetext_default If library 'dateutil' is not found return datetext_default and register exception. """ time_types = {'d':0, 'w':0, 'm':0, 'y':0} today = datetime.today() try: nb = int(display_since[:-1]) except: return datetext_default if display_since in [None, 'all']: return datetext_default if str(display_since[-1]) in time_types: time_type = str(display_since[-1]) else: return datetext_default # year if time_type == 'y': if (int(display_since[:-1]) > today.year - 1) or (int(display_since[:-1]) < 1): # 1 < nb years < 2008 return datetext_default else: final_nb_year = today.year - nb yesterday = today.replace(year=final_nb_year) # month elif time_type == 'm': try: from dateutil.relativedelta import relativedelta except ImportError: # The dateutil library is only recommended: if not # available, then send warning about this. register_exception(alert_admin=True) return datetext_default # obtain only the date: yyyy-mm-dd date_today = datetime.now().date() final_date = date_today - relativedelta(months=nb) yesterday = today.replace(year=final_date.year, month=final_date.month, day=final_date.day) # week elif time_type == 'w': delta = timedelta(weeks=nb) yesterday = today - delta # day elif time_type == 'd': delta = timedelta(days=nb) yesterday = today - delta return yesterday.strftime("%Y-%m-%d %H:%M:%S") def get_first_comments_or_remarks(recID=-1, ln=CFG_SITE_LANG, nb_comments='all', nb_reviews='all', voted=-1, reported=-1, user_info=None, show_reviews=False): """ Gets nb number comments/reviews or remarks. In the case of comments, will get both comments and reviews Comments and remarks sorted by most recent date, reviews sorted by highest helpful score @param recID: record id @param ln: language @param nb_comments: number of comment or remarks to get @param nb_reviews: number of reviews or remarks to get @param voted: 1 if user has voted for a remark @param reported: 1 if user has reported a comment or review @return: if comment, tuple (comments, reviews) both being html of first nb comments/reviews if remark, tuple (remakrs, None) """ _ = gettext_set_language(ln) warnings = [] voted = wash_url_argument(voted, 'int') reported = wash_url_argument(reported, 'int') ## check recID argument if type(recID) is not int: return () if recID >= 1: #comment or review. NB: suppressed reference to basket (handled in webbasket) if CFG_WEBCOMMENT_ALLOW_REVIEWS: res_reviews = query_retrieve_comments_or_remarks(recID=recID, display_order="hh", ranking=1, limit=nb_comments, user_info=user_info) nb_res_reviews = len(res_reviews) ## check nb argument if type(nb_reviews) is int and nb_reviews < len(res_reviews): first_res_reviews = res_reviews[:nb_reviews] else: first_res_reviews = res_reviews if CFG_WEBCOMMENT_ALLOW_COMMENTS: res_comments = query_retrieve_comments_or_remarks(recID=recID, display_order="od", ranking=0, limit=nb_reviews, user_info=user_info) nb_res_comments = len(res_comments) ## check nb argument if type(nb_comments) is int and nb_comments < len(res_comments): first_res_comments = res_comments[:nb_comments] else: first_res_comments = res_comments else: #error try: raise InvenioWebCommentError(_('%(recid)s is an invalid record ID', recid=recID)) except InvenioWebCommentError as exc: register_exception() body = webcomment_templates.tmpl_error(exc.message, ln) return body #errors.append(('ERR_WEBCOMMENT_RECID_INVALID', recID)) #!FIXME dont return error anywhere since search page # comment if recID >= 1: comments = reviews = "" if reported > 0: try: raise InvenioWebCommentWarning(_('Your feedback has been recorded, many thanks.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, 'green')) #warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED_GREEN_TEXT',)) elif reported == 0: try: raise InvenioWebCommentWarning(_('Your feedback could not be recorded, please try again.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED_RED_TEXT',)) if CFG_WEBCOMMENT_ALLOW_COMMENTS: # normal comments grouped_comments = group_comments_by_round(first_res_comments, ranking=0) comments = webcomment_templates.tmpl_get_first_comments_without_ranking(recID, ln, grouped_comments, nb_res_comments, warnings) if show_reviews: if CFG_WEBCOMMENT_ALLOW_REVIEWS: # ranked comments #calculate average score avg_score = calculate_avg_score(res_reviews) if voted > 0: try: raise InvenioWebCommentWarning(_('Your feedback has been recorded, many thanks.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, 'green')) #warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED_GREEN_TEXT',)) elif voted == 0: try: raise InvenioWebCommentWarning(_('Your feedback could not be recorded, please try again.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED_RED_TEXT',)) grouped_reviews = group_comments_by_round(first_res_reviews, ranking=0) reviews = webcomment_templates.tmpl_get_first_comments_with_ranking(recID, ln, grouped_reviews, nb_res_reviews, avg_score, warnings) return (comments, reviews) # remark else: return(webcomment_templates.tmpl_get_first_remarks(first_res_comments, ln, nb_res_comments), None) def group_comments_by_round(comments, ranking=0): """ Group comments by the round to which they belong """ comment_rounds = {} ordered_comment_round_names = [] for comment in comments: comment_round_name = ranking and comment[11] or comment[7] if comment_round_name not in comment_rounds: comment_rounds[comment_round_name] = [] ordered_comment_round_names.append(comment_round_name) comment_rounds[comment_round_name].append(comment) return [(comment_round_name, comment_rounds[comment_round_name]) \ for comment_round_name in ordered_comment_round_names] def calculate_avg_score(res): """ private function Calculate the avg score of reviews present in res @param res: tuple of tuple returned from query_retrieve_comments_or_remarks @return: a float of the average score rounded to the closest 0.5 """ c_star_score = 6 avg_score = 0.0 nb_reviews = 0 for comment in res: if comment[c_star_score] > 0: avg_score += comment[c_star_score] nb_reviews += 1 if nb_reviews == 0: return 0.0 avg_score = avg_score / nb_reviews avg_score_unit = avg_score - math.floor(avg_score) if avg_score_unit < 0.25: avg_score = math.floor(avg_score) elif avg_score_unit > 0.75: avg_score = math.floor(avg_score) + 1 else: avg_score = math.floor(avg_score) + 0.5 if avg_score > 5: avg_score = 5.0 return avg_score def perform_request_add_comment_or_remark(recID=0, uid=-1, action='DISPLAY', ln=CFG_SITE_LANG, msg=None, score=None, note=None, priority=None, reviews=0, comID=0, client_ip_address=None, editor_type='textarea', can_attach_files=False, subscribe=False, req=None, attached_files=None, warnings=None): """ Add a comment/review or remark @param recID: record id @param uid: user id @param action: 'DISPLAY' to display add form 'SUBMIT' to submit comment once form is filled 'REPLY' to reply to an existing comment @param ln: language @param msg: the body of the comment/review or remark @param score: star score of the review @param note: title of the review @param priority: priority of remark (int) @param reviews: boolean, if enabled will add a review, if disabled will add a comment @param comID: if replying, this is the comment id of the comment we are replying to @param editor_type: the kind of editor/input used for the comment: 'textarea', 'ckeditor' @param can_attach_files: if user can attach files to comments or not @param subscribe: if True, subscribe user to receive new comments by email @param req: request object. Used to register callback to send email notification @param attached_files: newly attached files to this comment, mapping filename to filepath @type attached_files: dict @param warnings: list of warning tuples (warning_text, warning_color) that should be considered @return: - html add form if action is display or reply - html successful added form if action is submit """ _ = gettext_set_language(ln) if warnings is None: warnings = [] actions = ['DISPLAY', 'REPLY', 'SUBMIT'] _ = gettext_set_language(ln) ## check arguments check_recID_is_in_range(recID, warnings, ln) if uid <= 0: try: raise InvenioWebCommentError(_('%(uid)s is an invalid user ID.', uid=uid)) except InvenioWebCommentError as exc: register_exception() body = webcomment_templates.tmpl_error(exc.message, ln) return body #errors.append(('ERR_WEBCOMMENT_UID_INVALID', uid)) return '' if attached_files is None: attached_files = {} user_contact_info = query_get_user_contact_info(uid) nickname = '' if user_contact_info: if user_contact_info[0]: nickname = user_contact_info[0] # show the form if action == 'DISPLAY': if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS: return webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings, can_attach_files=can_attach_files) elif not reviews and CFG_WEBCOMMENT_ALLOW_COMMENTS: return webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings, can_attach_files=can_attach_files) else: try: raise InvenioWebCommentError(_('Comments on records have been disallowed by the administrator.')) except InvenioWebCommentError as exc: register_exception(req=req) body = webcomment_templates.tmpl_error(exc.message, ln) return body #errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',)) elif action == 'REPLY': if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS: try: raise InvenioWebCommentError(_('Cannot reply to a review.')) except InvenioWebCommentError as exc: register_exception(req=req) body = webcomment_templates.tmpl_error(exc.message, ln) return body #errors.append(('ERR_WEBCOMMENT_REPLY_REVIEW',)) return webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings, can_attach_files=can_attach_files) elif not reviews and CFG_WEBCOMMENT_ALLOW_COMMENTS: textual_msg = msg if comID > 0: comment = query_get_comment(comID) if comment: user_info = get_user_info(comment[2]) if user_info: date_creation = convert_datetext_to_dategui(str(comment[4])) # Build two msg: one mostly textual, the other one with HTML markup, for the CkEditor. msg = _("%(x_name)s wrote on %(x_date)s:")% {'x_name': user_info[2], 'x_date': date_creation} textual_msg = msg # 1 For CkEditor input msg += '\n\n' msg += comment[3] msg = email_quote_txt(text=msg) # Now that we have a text-quoted version, transform into # something that CkEditor likes, using <blockquote> that # do still enable users to insert comments inline msg = email_quoted_txt2html(text=msg, indent_html=('<blockquote><div>', ' </div></blockquote>'), linebreak_html=" <br/>", indent_block=False) # Add some space for users to easily add text # around the quoted message msg = '<br/>' + msg + '<br/>' # Due to how things are done, we need to # escape the whole msg again for the editor msg = cgi.escape(msg) # 2 For textarea input textual_msg += "\n\n" textual_msg += comment[3] textual_msg = email_quote_txt(text=textual_msg) return webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings, textual_msg, can_attach_files=can_attach_files, reply_to=comID) else: try: raise InvenioWebCommentError(_('Comments on records have been disallowed by the administrator.')) except InvenioWebCommentError as exc: register_exception(req=req) body = webcomment_templates.tmpl_error(exc.message, ln) return body #errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',)) # check before submitting form elif action == 'SUBMIT': if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS: if note.strip() in ["", "None"] and not CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS: try: raise InvenioWebCommentWarning(_('You must enter a title.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_ADD_NO_TITLE',)) if score == 0 or score > 5: try: raise InvenioWebCommentWarning(_('You must choose a score.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(("WRN_WEBCOMMENT_ADD_NO_SCORE",)) if msg.strip() in ["", "None"] and not CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS: try: raise InvenioWebCommentWarning(_('You must enter a text.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_ADD_NO_BODY',)) # if no warnings, submit if len(warnings) == 0: if reviews: if check_user_can_review(recID, client_ip_address, uid): success = query_add_comment_or_remark(reviews, recID=recID, uid=uid, msg=msg, note=note, score=score, priority=0, client_ip_address=client_ip_address, editor_type=editor_type, req=req, reply_to=comID) else: try: raise InvenioWebCommentWarning(_('You already wrote a review for this record.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append('WRN_WEBCOMMENT_CANNOT_REVIEW_TWICE') success = 1 else: if check_user_can_comment(recID, client_ip_address, uid): success = query_add_comment_or_remark(reviews, recID=recID, uid=uid, msg=msg, note=note, score=score, priority=0, client_ip_address=client_ip_address, editor_type=editor_type, req=req, reply_to=comID, attached_files=attached_files) if success > 0 and subscribe: subscribe_user_to_discussion(recID, uid) else: try: raise InvenioWebCommentWarning(_('You already posted a comment short ago. Please retry later.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append('WRN_WEBCOMMENT_TIMELIMIT') success = 1 if success > 0: if CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL > 0: notify_admin_of_new_comment(comID=success) return webcomment_templates.tmpl_add_comment_successful(recID, ln, reviews, warnings, success) else: try: raise InvenioWebCommentError(_('Failed to insert your comment to the database. Please try again.')) except InvenioWebCommentError as exc: register_exception(req=req) body = webcomment_templates.tmpl_error(exc.message, ln) return body #errors.append(('ERR_WEBCOMMENT_DB_INSERT_ERROR')) # if are warnings or if inserting comment failed, show user where warnings are if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS: return webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings, can_attach_files=can_attach_files) else: return webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings, can_attach_files=can_attach_files) # unknown action send to display else: try: raise InvenioWebCommentWarning(_('Unknown action --> showing you the default add comment form.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning', req=req) warnings.append((exc.message, '')) #warnings.append(('WRN_WEBCOMMENT_ADD_UNKNOWN_ACTION',)) if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS: return webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, ln, msg, score, note, warnings, can_attach_files=can_attach_files) else: return webcomment_templates.tmpl_add_comment_form(recID, uid, ln, msg, warnings, can_attach_files=can_attach_files) return '' def notify_admin_of_new_comment(comID): """ Sends an email to the admin with details regarding comment with ID = comID """ comment = query_get_comment(comID) if len(comment) > 0: (comID2, id_bibrec, id_user, body, date_creation, star_score, nb_votes_yes, nb_votes_total, title, nb_abuse_reports, round_name, restriction) = comment else: return user_info = query_get_user_contact_info(id_user) if len(user_info) > 0: (nickname, email, last_login) = user_info if not len(nickname) > 0: nickname = email.split('@')[0] else: nickname = email = last_login = "ERROR: Could not retrieve" review_stuff = ''' Star score = %s Title = %s''' % (star_score, title) washer = EmailWasher() try: body = washer.wash(body) except: body = cgi.escape(body) record_info = webcomment_templates.tmpl_email_new_comment_admin(id_bibrec) out = ''' The following %(comment_or_review)s has just been posted (%(date)s). AUTHOR: Nickname = %(nickname)s Email = %(email)s User ID = %(uid)s RECORD CONCERNED: Record ID = %(recID)s URL = <%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(comments_or_reviews)s/> %(record_details)s %(comment_or_review_caps)s: %(comment_or_review)s ID = %(comID)s %(review_stuff)s Body = <---------------> %(body)s <---------------> ADMIN OPTIONS: To moderate the %(comment_or_review)s go to %(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(comments_or_reviews)s/display?%(arguments)s ''' % \ { 'comment_or_review' : star_score > 0 and 'review' or 'comment', 'comment_or_review_caps': star_score > 0 and 'REVIEW' or 'COMMENT', 'comments_or_reviews' : star_score > 0 and 'reviews' or 'comments', 'date' : date_creation, 'nickname' : nickname, 'email' : email, 'uid' : id_user, 'recID' : id_bibrec, 'record_details' : record_info, 'comID' : comID2, 'review_stuff' : star_score > 0 and review_stuff or "", 'body' : body.replace('<br />','\n'), 'siteurl' : CFG_SITE_URL, 'CFG_SITE_RECORD' : CFG_SITE_RECORD, 'arguments' : 'ln=en&do=od#%s' % comID } from_addr = '%s WebComment <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL) comment_collection = get_comment_collection(comID) to_addrs = get_collection_moderators(comment_collection) rec_collection = guess_primary_collection_of_a_record(id_bibrec) report_nums = get_fieldvalues(id_bibrec, "037__a") report_nums += get_fieldvalues(id_bibrec, "088__a") report_nums = ', '.join(report_nums) subject = "A new comment/review has just been posted [%s|%s]" % (rec_collection, report_nums) send_email(from_addr, to_addrs, subject, out) def check_recID_is_in_range(recID, warnings=[], ln=CFG_SITE_LANG): """ Check that recID is >= 0 @param recID: record id @param warnings: list of warning tuples (warning_text, warning_color) @return: tuple (boolean, html) where boolean (1=true, 0=false) and html is the body of the page to display if there was a problem """ _ = gettext_set_language(ln) try: recID = int(recID) except: pass if type(recID) is int: if recID > 0: from invenio.legacy.search_engine import record_exists success = record_exists(recID) if success == 1: return (1,"") else: try: if success == -1: status = 'deleted' raise InvenioWebCommentWarning(_('The record has been deleted.')) else: status = 'inexistant' raise InvenioWebCommentWarning(_('Record ID %(x_rec)s does not exist in the database.', x_rec=recID)) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, '')) #warnings.append(('ERR_WEBCOMMENT_RECID_INEXISTANT', recID)) return (0, webcomment_templates.tmpl_record_not_found(status=status, recID=recID, ln=ln)) elif recID == 0: try: raise InvenioWebCommentWarning(_('No record ID was given.')) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, '')) #warnings.append(('ERR_WEBCOMMENT_RECID_MISSING',)) return (0, webcomment_templates.tmpl_record_not_found(status='missing', recID=recID, ln=ln)) else: try: raise InvenioWebCommentWarning(_('Record ID %(recid)s is an invalid ID.', recid=recID)) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, '')) #warnings.append(('ERR_WEBCOMMENT_RECID_INVALID', recID)) return (0, webcomment_templates.tmpl_record_not_found(status='invalid', recID=recID, ln=ln)) else: try: raise InvenioWebCommentWarning(_('Record ID %(recid)s is not a number.', recid=recID)) except InvenioWebCommentWarning as exc: register_exception(stream='warning') warnings.append((exc.message, '')) #warnings.append(('ERR_WEBCOMMENT_RECID_NAN', recID)) return (0, webcomment_templates.tmpl_record_not_found(status='nan', recID=recID, ln=ln)) def check_int_arg_is_in_range(value, name, gte_value, lte_value=None): """ Check that variable with name 'name' >= gte_value and optionally <= lte_value @param value: variable value @param name: variable name @param errors: list of error tuples (error_id, value) @param gte_value: greater than or equal to value @param lte_value: less than or equal to value @return: boolean (1=true, 0=false) """ if type(value) is not int: try: raise InvenioWebCommentError('%s is not a number.' % value) except InvenioWebCommentError as exc: register_exception() body = webcomment_templates.tmpl_error(exc.message) return body #errors.append(('ERR_WEBCOMMENT_ARGUMENT_NAN', value)) return 0 if value < gte_value: try: raise InvenioWebCommentError('%s invalid argument.' % value) except InvenioWebCommentError as exc: register_exception() body = webcomment_templates.tmpl_error(exc.message) return body #errors.append(('ERR_WEBCOMMENT_ARGUMENT_INVALID', value)) return 0 if lte_value: if value > lte_value: try: raise InvenioWebCommentError('%s invalid argument.' % value) except InvenioWebCommentError as exc: register_exception() body = webcomment_templates.tmpl_error(exc.message) return body #errors.append(('ERR_WEBCOMMENT_ARGUMENT_INVALID', value)) return 0 return 1 def get_mini_reviews(recid, ln=CFG_SITE_LANG): """ Returns the web controls to add reviews to a record from the detailed record pages mini-panel. @param recid: the id of the displayed record @param ln: the user's language """ if CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS: action = 'SUBMIT' else: action = 'DISPLAY' reviews = query_retrieve_comments_or_remarks(recid, ranking=1) return webcomment_templates.tmpl_mini_review(recid, ln, action=action, avg_score=calculate_avg_score(reviews), nb_comments_total=len(reviews)) def check_user_can_view_comments(user_info, recid): """Check if the user is authorized to view comments for given recid. Returns the same type as acc_authorize_action """ # Check user can view the record itself first (auth_code, auth_msg) = check_user_can_view_record(user_info, recid) if auth_code: return (auth_code, auth_msg) # Check if user can view the comments ## But first can we find an authorization for this case action, ## for this collection? record_primary_collection = guess_primary_collection_of_a_record(recid) return acc_authorize_action(user_info, 'viewcomment', authorized_if_no_roles=True, collection=record_primary_collection) def check_user_can_view_comment(user_info, comid, restriction=None): """Check if the user is authorized to view a particular comment, given the comment restriction. Note that this function does not check if the record itself is restricted to the user, which would mean that the user should not see the comment. You can omit 'comid' if you already know the 'restriction' @param user_info: the user info object @param comid: the comment id of that we want to check @param restriction: the restriction applied to given comment (if known. Otherwise retrieved automatically) @return: the same type as acc_authorize_action """ if restriction is None: comment = query_get_comment(comid) if comment: restriction = comment[11] else: return (1, 'Comment %i does not exist' % comid) if restriction == "": return (0, '') return acc_authorize_action(user_info, 'viewrestrcomment', status=restriction) def check_user_can_send_comments(user_info, recid): """Check if the user is authorized to comment the given recid. This function does not check that user can view the record or view the comments Returns the same type as acc_authorize_action """ ## First can we find an authorization for this case, action + collection record_primary_collection = guess_primary_collection_of_a_record(recid) return acc_authorize_action(user_info, 'sendcomment', authorized_if_no_roles=True, collection=record_primary_collection) def check_comment_belongs_to_record(comid, recid): """ Return True if the comment is indeed part of given record (even if comment or/and record have been "deleted"). Else return False. @param comid: the id of the comment to check membership @param recid: the recid of the record we want to check if comment belongs to """ query = """SELECT id_bibrec from "cmtRECORDCOMMENT" WHERE id=%s""" params = (comid,) res = run_sql(query, params) if res and res[0][0] == recid: return True return False def check_user_can_attach_file_to_comments(user_info, recid): """Check if the user is authorized to attach a file to comments for given recid. This function does not check that user can view the comments or send comments. Returns the same type as acc_authorize_action """ ## First can we find an authorization for this case action, for ## this collection? record_primary_collection = guess_primary_collection_of_a_record(recid) return acc_authorize_action(user_info, 'attachcommentfile', authorized_if_no_roles=False, collection=record_primary_collection) def toggle_comment_visibility(uid, comid, collapse, recid): """ Toggle the visibility of the given comment (collapse) for the given user. Return the new visibility @param uid: the user id for which the change applies @param comid: the comment id to close/open @param collapse: if the comment is to be closed (1) or opened (0) @param recid: the record id to which the comment belongs @return: if the comment is visible or not after the update """ # We rely on the client to tell if comment should be collapsed or # developed, to ensure consistency between our internal state and # client state. Even if not strictly necessary, we store the # record ID for quicker retrieval of the collapsed comments of a # given discussion page. To prevent unnecessary population of the # table, only one distinct tuple (record ID, comment ID, user ID) # can be inserted (due to table definition). For the same purpose # we also check that comment to collapse exists, and corresponds # to an existing record: we cannot rely on the recid found as part # of the URL, as no former check is done. This rule is not applied # when deleting an entry, as in the worst case no line would be # removed. For optimized retrieval of row to delete, the id_bibrec # column is used, though not strictly necessary. if collapse: query = """SELECT id_bibrec from "cmtRECORDCOMMENT" WHERE id=%s""" params = (comid,) res = run_sql(query, params) if res: query = """INSERT INTO "cmtCOLLAPSED" (id_bibrec, "id_cmtRECORDCOMMENT", id_user) VALUES (%s, %s, %s)""" params = (res[0][0], comid, uid) run_sql(query, params) return True else: query = """DELETE FROM "cmtCOLLAPSED" WHERE "id_cmtRECORDCOMMENT"=%s and id_user=%s and id_bibrec=%s""" params = (comid, uid, recid) run_sql(query, params) return False def get_user_collapsed_comments_for_record(uid, recid): """ Get the comments collapsed for given user on given recid page """ # Collapsed state is not an attribute of cmtRECORDCOMMENT table # (vary per user) so it cannot be found when querying for the # comment. We must therefore provide a efficient way to retrieve # the collapsed state for a given discussion page and user. query = """SELECT "id_cmtRECORDCOMMENT" from "cmtCOLLAPSED" WHERE id_user=%s and id_bibrec=%s""" params = (uid, recid) return [res[0] for res in run_sql(query, params)] def is_comment_deleted(comid): """ Return True of the comment is deleted. Else False @param comid: ID of comment to check """ query = """SELECT status from "cmtRECORDCOMMENT" WHERE id=%s""" params = (comid,) res = run_sql(query, params) if res and res[0][0] != 'ok': return True return False def perform_display_your_comments(user_info, page_number=1, selected_order_by_option="lcf", selected_display_number_option="all", selected_display_format_option="rc", ln=CFG_SITE_LANG): """ Display all comments submitted by the user. @TODO: support reviews too @param user_info: standard user info object. @param comments: ordered list of tuples (id_bibrec, comid, date_creation, body, status, in_reply_to_id_cmtRECORDCOMMENT) @param page_number: page on which the user is. @type page_number: integer @param selected_order_by_option: seleccted ordering option. Can be one of: - ocf: Oldest comment first - lcf: Latest comment first - grof: Group by record, oldest commented first - grlf: Group by record, latest commented first @type selected_order_by_option: string @param selected_display_number_option: number of results to show per page. Can be a string-digit or 'all'. @type selected_display_number_option: string @param selected_display_format_option: how to show records. Can be one of: - rc: Records and comments - ro: Records only - co: Comments only @type selected_display_format_option: string @ln: language @type ln: string """ query_params = "" nb_total_pages = 0 if selected_display_format_option in ('rc', 'co'): nb_total_results = run_sql("""SELECT count(id) from "cmtRECORDCOMMENT" WHERE id_user=%s AND star_score = 0""", \ (user_info['uid'], ))[0][0] else: if selected_order_by_option in ('grlf', 'grof'): nb_total_results = run_sql("""SELECT count(distinct(id_bibrec)) from "cmtRECORDCOMMENT" WHERE id_user=%s AND star_score = 0""", \ (user_info['uid'], ))[0][0] else: nb_total_results = run_sql("""SELECT count(id_bibrec) from "cmtRECORDCOMMENT" WHERE id_user=%s AND star_score = 0""", \ (user_info['uid'], ))[0][0] if page_number < 1: page_number = 1 if selected_display_number_option != 'all' and \ not selected_display_number_option.isdigit(): # must be some garbage selected_display_number_option = 'all' query = '' if selected_order_by_option == "lcf": query_params += " ORDER BY date_creation DESC" elif selected_order_by_option == "ocf": query_params += " ORDER BY date_creation ASC" elif selected_order_by_option == "grlf": query = """SELECT cmt.id_bibrec, cmt.id, cmt.date_creation, cmt.body, cmt.status, cmt."in_reply_to_id_cmtRECORDCOMMENT" FROM "cmtRECORDCOMMENT" as cmt left join (SELECT max(date_creation) as maxdatecreation, id_bibrec FROM "cmtRECORDCOMMENT" WHERE id_user=%s AND star_score = 0 GROUP BY id_bibrec) as grp on cmt.id_bibrec = grp.id_bibrec WHERE id_user=%s AND star_score = 0 ORDER BY grp.maxdatecreation DESC, cmt.date_creation DESC""" elif selected_order_by_option == "grof": query = """SELECT cmt.id_bibrec, cmt.id, cmt.date_creation, cmt.body, cmt.status, cmt."in_reply_to_id_cmtRECORDCOMMENT" FROM "cmtRECORDCOMMENT" as cmt left join (SELECT min(date_creation) as mindatecreation, id_bibrec FROM "cmtRECORDCOMMENT" WHERE id_user=%s AND star_score = 0 GROUP BY id_bibrec) as grp on cmt.id_bibrec = grp.id_bibrec WHERE id_user=%s AND star_score = 0 ORDER BY grp.mindatecreation ASC""" if selected_display_number_option.isdigit(): selected_display_number_option_as_int = int(selected_display_number_option) if selected_display_number_option_as_int < 5: selected_display_number_option_as_int = 5 selected_display_number_option = str(selected_display_number_option_as_int) from_index = (page_number - 1) * int(selected_display_number_option) query_params += ' LIMIT ' + \ str(from_index) + \ ',' + \ str(int(selected_display_number_option)) nb_total_pages = int(math.ceil(float(nb_total_results) / selected_display_number_option_as_int)) if selected_order_by_option in ("grlf", "grof"): res = run_sql(query + query_params, (user_info['uid'], user_info['uid'])) else: res = run_sql("""SELECT id_bibrec, id, date_creation, body, status, "in_reply_to_id_cmtRECORDCOMMENT" FROM "cmtRECORDCOMMENT" WHERE id_user=%s AND star_score = 0""" + query_params, (user_info['uid'], )) return webcomment_templates.tmpl_your_comments(user_info, res, page_number=page_number, selected_order_by_option=selected_order_by_option, selected_display_number_option=selected_display_number_option, selected_display_format_option=selected_display_format_option, nb_total_results=nb_total_results, nb_total_pages=nb_total_pages, ln=ln) diff --git a/invenio/modules/comments/models.py b/invenio/modules/comments/models.py index 577db30a2..75fd9c40f 100644 --- a/invenio/modules/comments/models.py +++ b/invenio/modules/comments/models.py @@ -1,180 +1,186 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. # Copyright (C) 2011, 2012, 2013, 2015 CERN. # # Invenio is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # Invenio is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Invenio; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """WebComment database models.""" from invenio.base.signals import record_after_update from invenio.ext.sqlalchemy import db from invenio.ext.sqlalchemy.utils import session_manager from invenio.modules.accounts.models import User from invenio.modules.records.models import Record as Bibrec from sqlalchemy import event class CmtRECORDCOMMENT(db.Model): """Represents a CmtRECORDCOMMENT record.""" __tablename__ = 'cmtRECORDCOMMENT' id = db.Column(db.Integer(15, unsigned=True), nullable=False, primary_key=True, autoincrement=True) id_bibrec = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(Bibrec.id), nullable=False, server_default='0') id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), nullable=False, server_default='0') title = db.Column(db.String(255), nullable=False, server_default='') body = db.Column(db.Text, nullable=False) date_creation = db.Column(db.DateTime, nullable=False, server_default='1900-01-01 00:00:00') star_score = db.Column(db.TinyInteger(5, unsigned=True), nullable=False, server_default='0') - nb_votes_yes = db.Column(db.Integer(10), nullable=False, server_default='0') + nb_votes_yes = db.Column(db.Integer(10), nullable=False, + server_default='0') nb_votes_total = db.Column(db.Integer(10, unsigned=True), nullable=False, server_default='0') nb_abuse_reports = db.Column(db.Integer(10), nullable=False, server_default='0') status = db.Column(db.Char(2), nullable=False, index=True, server_default='ok') round_name = db.Column(db.String(255), nullable=False, server_default='') restriction = db.Column(db.String(50), nullable=False, server_default='') in_reply_to_id_cmtRECORDCOMMENT = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(id), nullable=False, server_default='0') reply_order_cached_data = db.Column(db.Binary, nullable=True) bibrec = db.relationship(Bibrec, backref='recordcomments') user = db.relationship(User, backref='recordcomments') replies = db.relationship('CmtRECORDCOMMENT', backref=db.backref( 'parent', remote_side=[id], order_by=date_creation)) @property def is_deleted(self): """Check if is deleted.""" return self.status != 'ok' def is_collapsed(self, id_user): """Return true if the comment is collapsed by user.""" return CmtCOLLAPSED.query.filter(db.and_( CmtCOLLAPSED.id_bibrec == self.id_bibrec, CmtCOLLAPSED.id_cmtRECORDCOMMENT == self.id, CmtCOLLAPSED.id_user == id_user)).count() > 0 @session_manager def collapse(self, id_user): """Collapse comment beloging to user.""" c = CmtCOLLAPSED(id_bibrec=self.id_bibrec, id_cmtRECORDCOMMENT=self.id, id_user=id_user) db.session.add(c) db.session.commit() def expand(self, id_user): """Expand comment beloging to user.""" CmtCOLLAPSED.query.filter(db.and_( CmtCOLLAPSED.id_bibrec == self.id_bibrec, CmtCOLLAPSED.id_cmtRECORDCOMMENT == self.id, CmtCOLLAPSED.id_user == id_user)).delete(synchronize_session=False) __table_args__ = (db.Index('cmtRECORDCOMMENT_reply_order_cached_data', reply_order_cached_data, mysql_length=40), db.Model.__table_args__) + @classmethod + def count(cls, *criteria, **filters): + """Count how many comments.""" + return cls.query.filter(*criteria).filter_by(**filters).count() + @event.listens_for(CmtRECORDCOMMENT, 'after_insert') def after_insert(mapper, connection, target): """Update reply order cache and send record-after-update signal.""" record_after_update.send(CmtRECORDCOMMENT, recid=target.id_bibrec) from .api import get_reply_order_cache_data if target.in_reply_to_id_cmtRECORDCOMMENT > 0: parent = CmtRECORDCOMMENT.query.get( target.in_reply_to_id_cmtRECORDCOMMENT) if parent: trans = connection.begin() parent_reply_order = parent.reply_order_cached_data \ if parent.reply_order_cached_data else '' parent_reply_order += get_reply_order_cache_data(target.id) connection.execute( db.update(CmtRECORDCOMMENT.__table__). where(CmtRECORDCOMMENT.id == parent.id). values(reply_order_cached_data=parent_reply_order)) trans.commit() class CmtACTIONHISTORY(db.Model): """Represents a CmtACTIONHISTORY record.""" __tablename__ = 'cmtACTIONHISTORY' id_cmtRECORDCOMMENT = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(CmtRECORDCOMMENT.id), nullable=True, primary_key=True) id_bibrec = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(Bibrec.id), nullable=True, primary_key=True) id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), nullable=True, primary_key=True) client_host = db.Column(db.Integer(10, unsigned=True), nullable=True) action_time = db.Column(db.DateTime, nullable=False, server_default='1900-01-01 00:00:00') action_code = db.Column(db.Char(1), nullable=False, index=True) recordcomment = db.relationship(CmtRECORDCOMMENT, backref='actionhistory') bibrec = db.relationship(Bibrec) user = db.relationship(User) class CmtSUBSCRIPTION(db.Model): """Represents a CmtSUBSCRIPTION record.""" __tablename__ = 'cmtSUBSCRIPTION' id_bibrec = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(Bibrec.id), nullable=False, primary_key=True) id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), nullable=False, primary_key=True) creation_time = db.Column(db.DateTime, nullable=False, server_default='1900-01-01 00:00:00') bibrec = db.relationship(Bibrec) user = db.relationship(User, backref='comment_subscriptions') class CmtCOLLAPSED(db.Model): """Represents a CmtCOLLAPSED record.""" __tablename__ = 'cmtCOLLAPSED' id_bibrec = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(Bibrec.id), primary_key=True) id_cmtRECORDCOMMENT = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(CmtRECORDCOMMENT.id), primary_key=True) id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), primary_key=True) __all__ = ('CmtRECORDCOMMENT', 'CmtACTIONHISTORY', 'CmtSUBSCRIPTION', 'CmtCOLLAPSED') diff --git a/invenio/modules/comments/utils.py b/invenio/modules/comments/utils.py index 3da217a6a..ddd0a6efb 100644 --- a/invenio/modules/comments/utils.py +++ b/invenio/modules/comments/utils.py @@ -1,50 +1,56 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. -# Copyright (C) 2014 CERN. +# Copyright (C) 2014, 2015 CERN. # # Invenio is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # Invenio is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Invenio; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """Comment utility functions.""" from flask import request +from invenio.modules.comments.models import CmtRECORDCOMMENT + def comments_nb_counts(): """Get number of comments for the record `recid`.""" recid = request.view_args.get('recid') if recid is None: return elif recid == 0: return 0 else: - from invenio.legacy.webcomment.adminlib import get_nb_comments - - return get_nb_comments(recid, count_deleted=False) + return CmtRECORDCOMMENT.count(*[ + CmtRECORDCOMMENT.id_bibrec == recid, + CmtRECORDCOMMENT.star_score == 0, + CmtRECORDCOMMENT.status.notin_('dm', 'da') + ]) def reviews_nb_counts(): """Get number of reviews for the record `recid`.""" recid = request.view_args.get('recid') if recid is None: return elif recid == 0: return 0 else: - from invenio.legacy.webcomment.adminlib import get_nb_reviews - - return get_nb_reviews(recid, count_deleted=False) + return CmtRECORDCOMMENT.count(*[ + CmtRECORDCOMMENT.id_bibrec == recid, + CmtRECORDCOMMENT.star_score > 0, + CmtRECORDCOMMENT.status.notin_(['dm', 'da']) + ]) diff --git a/invenio/modules/records/testsuite/functions/get_number_of_comments.py b/invenio/modules/records/testsuite/functions/get_number_of_comments.py index 1310505f8..73e9a4c58 100644 --- a/invenio/modules/records/testsuite/functions/get_number_of_comments.py +++ b/invenio/modules/records/testsuite/functions/get_number_of_comments.py @@ -1,31 +1,37 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. -# Copyright (C) 2013, 2014 CERN. +# Copyright (C) 2013, 2014, 2015 CERN. # # Invenio is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # Invenio is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Invenio; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""Get number of comments.""" + def get_number_of_comments(recid): """ - Returns number of comments for given record. + Return number of comments for given record. :param recid: :return: Number of comments """ - from invenio.legacy.webcomment.adminlib import get_nb_comments + from invenio.modules.comments.models import CmtRECORDCOMMENT if recid: - return get_nb_comments(recid) + filters = [ + CmtRECORDCOMMENT.id_bibrec == recid, + CmtRECORDCOMMENT.star_score == 0 + ] + return CmtRECORDCOMMENT.count(*filters) diff --git a/invenio/modules/records/testsuite/functions/get_number_of_reviews.py b/invenio/modules/records/testsuite/functions/get_number_of_reviews.py index 24354636d..2c700267c 100644 --- a/invenio/modules/records/testsuite/functions/get_number_of_reviews.py +++ b/invenio/modules/records/testsuite/functions/get_number_of_reviews.py @@ -1,31 +1,35 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. -# Copyright (C) 2013, 2014 CERN. +# Copyright (C) 2013, 2014, 2015 CERN. # # Invenio is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # Invenio is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Invenio; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""Testsuite.""" + def get_number_of_reviews(recid): - """ - Returns number of reviews for given record. + """Return number of reviews for given record. :param recid: :return: Number of reviews """ - from invenio.legacy.webcomment.adminlib import get_nb_reviews + from invenio.modules.comments.models import CmtRECORDCOMMENT if recid: - return get_nb_reviews(recid) + return CmtRECORDCOMMENT.count(*[ + CmtRECORDCOMMENT.id_bibrec == recid, + CmtRECORDCOMMENT.star_score > 0 + ])