diff --git a/modules/webjournal/etc/AtlantisTimes.css b/modules/webjournal/etc/AtlantisTimes.css index 152b74300..c0caba305 100644 --- a/modules/webjournal/etc/AtlantisTimes.css +++ b/modules/webjournal/etc/AtlantisTimes.css @@ -1,328 +1,332 @@ @charset "UTF-8"; #container { padding: 0pt; width: 800px; margin-right: auto; margin-left: auto; background-color: #FFFFFF; margin-top: 0px; margin-bottom: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; min-height: 800px; } #headerBox { background-image: url(journal_header.png); background-repeat: no-repeat; height: 69px; background-position: left top; padding-top: 10px; padding-right: 20px; padding-left: 20px; padding-bottom: 0px; text-align: center; } #footerBox { background-image: url(journal_footer.png); background-repeat: no-repeat; height: 70px; background-position: left top; padding-top: 10px; padding-right: 20px; padding-left: 20px; padding-bottom: 0px; text-align: center; } #imprint { visibility: hidden; position: absolute; } h1.journalTitle { font-size: 3.4em; color: #000000; margin: 0px; padding: 0px; text-transform: uppercase; font-weight: lighter; } #navigationMenu { border-top-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-bottom-style: solid; border-top-color: #666666; border-bottom-color: #666666; padding: 4px; text-align: center; margin-top: 5px; margin-bottom: 10px; } #navigationMenu a:link, #navigationMenu a:visited { color: #333333; text-transform: uppercase; font-weight: lighter; font-size: large; letter-spacing: 0.2em; margin-right: 10px; margin-left: 10px; padding: 4px; margin-top: 5px; margin-bottom: 5px; } h2.rightColumnHeader { font-size: large; color: #333333; text-transform: uppercase; font-weight: lighter; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #999999; } #rightColumn { width: 200px; border-left-width: 1px; border-left-style: solid; border-left-color: #666666; font-size: small; } #rightColumn ul { padding-left: 0px; margin-left: 3px; list-style-position: inside; } #searchField { width: 120px; } #webjournal .header { font-size: small; font-weight: lighter; font-style: italic; color: #333333; } #webjournal .footer { font-size: x-small; color: #333333; text-align: center; } /* Images in detailed articles */ .imageScale, .imageScale img { width:280px; } /* First article image on index page. We cannot reuse the phl, ph and phr classes to draw the border, or the rendering of the index page on the HTML alert will fail*/ .featuredImageScale, .featuredImageScale img { width:300px; /*Also check bfe_webjournal_MainArticleOverview, parameter 'image_px_width' */ border:1px solid #999999; margin-bottom:6px; margin-right:12px; margin-top:0.2em; padding:2px; } /* Other articles images on index page. We cannot reuse the phl, ph and phr classes to draw the border, or the rendering of the index page on the HTML alert will fail */ .featuredImageScaleSmall, .featuredImageScaleSmall img { width:200px; /*Also check bfe_webjournal_MainArticleOverview, parameter 'small_image_px_width' */ border:1px solid #999999; margin-bottom:6px; margin-right:12px; margin-top:0.2em; padding:2px; } #webjournal { background-color: #FFFFFF; padding: 0px; font-family: Georgia, "Times New Roman", Times, serif; } #webjournal a:link { color: #000; text-decoration: none; } #webjournal a:visited{ color: #3C2D3A; text-decoration: none; } #webjournal a:hover { color: #000; text-decoration: underline; } .contentBox { background-image: url(journal_content.png); background-repeat: repeat-y; background-position: left top; padding-top: 0px; padding-right: 20px; padding-bottom: 0px; padding-left: 20px; } #footerBox2 { background-image: url(journal_footer2.png); background-repeat: no-repeat; height: 70px; background-position: left top; padding-top: 10px; padding-right: 20px; padding-left: 20px; padding-bottom: 0px; text-align: center; } .articleTitle { font-weight: lighter; margin-bottom: 5px; margin-top: 10px; text-align: left; } h2 { font-size: xx-large } h3 { font-size: x-large } div.new, h3.new, h2.new { background:transparent url(journal_new.png) no-repeat scroll left top; padding-left:25px; } .articleBody { font-size: medium; font-weight: lighter; text-align: justify; } .articleHeader { clear:both; font-size:100%; font-weight:600; margin-bottom:4px; margin-top:0pt; display: block; } .subNavigationMenuItem { font-size: small; font-weight: lighter; color: #333333; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #999999; margin-top: 5px; padding-bottom: 2px; } #articles { padding-right: 8px; padding-left: 8px; } .readMore { font-size: small; color: #333333; text-decoration: none; } .selectedNavigationPage { background-color: #CCCCCC; } a.selectedNavigationPage:hover { text-decoration:none; } /* Centered container for image + caption */ .phwithcaption { width:280px; margin:auto; border:1px solid #999; background-color:#FFF; clear:both; font-size:80%; color:#555; padding:2px; text-align:center; } /* Left-aligned container for image + caption */ .phlwithcaption { background-color:#FFFFFF; border:1px solid #999999; clear:left; color:#555555; float:left; font-size:80%; margin-right:24px; max-width:280px; padding:2px; } /* Right-aligned container for image + caption */ .phrwithcaption { background-color:#FFFFFF; border:1px solid #999999; color:#555555; float:right; font-size:80%; margin-left:24px; margin-top:24px; max-width:280px; padding:2px; display:block; } /* Centered container for image without caption */ .ph { background-color:#FFFFFF; border:1px solid #999999; margin-bottom:3px; margin-top:6px; padding:2px; } /* Left-aligned container for image without caption */ .phl { background-color:#FFFFFF; border:1px solid #999999; clear:left; float:left; margin-bottom:6px; margin-right:12px; margin-top:0.2em; padding:2px; } /* Right-aligned container for image without caption */ .phr { background-color:#FFFFFF; border:1px solid #999999; clear:right; float:right; margin-bottom:6px; margin-left:12px; margin-top:0.2em; padding:2px; } .caption { color:#555555; font-size:70%; } ul.whatsNew { line-height:1.2em; list-style-position:inside; list-style-type:none; font-weight:700; } ul.whatsNew li { margin-bottom: 15px } ul.whatsNewItem { list-style-image:none; list-style-position:inside; list-style-type:circle; font-weight:normal; } ul.whatsNewItem li { margin-bottom: 0px } a.rssLink{ font-size:small; } a.rssLink img{ border:none; padding-right:4px; +} +.htmlalertheader { + text-align:center; + background-color: #fff; } \ No newline at end of file diff --git a/modules/webjournal/lib/webjournal_templates.py b/modules/webjournal/lib/webjournal_templates.py index 39260a71d..d8a4a9770 100644 --- a/modules/webjournal/lib/webjournal_templates.py +++ b/modules/webjournal/lib/webjournal_templates.py @@ -1,692 +1,712 @@ # -*- coding: utf-8 -*- ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """ WebJournal templates - Defines the look of various parts of the WebJournal modules. Most customizations will however be done through BibFormat format templates files. """ import os from invenio.config import \ CFG_SITE_SUPPORT_EMAIL, \ CFG_ETCDIR, \ CFG_SITE_URL, \ CFG_SITE_LANG from invenio.messages import gettext_set_language from invenio.webpage import page from invenio.webjournal_utils import \ get_number_of_articles_for_issue, \ get_release_datetime, \ get_announcement_datetime, \ get_issue_number_display class Template: """Templating class, refer to bibformat.py for examples of call""" def tmpl_webjournal_missing_info_box(self, ln, title, msg_title, msg): """ returns a box indicating that the given journal was not found on the server, leaving the opportunity to select an existing journal from a list. """ _ = gettext_set_language(ln) box_title = msg_title box_text = msg box_list_title = _("Available Journals") # todo: move to DB call find_journals = lambda path: [entry for entry in os.listdir(str(path)) \ if os.path.isdir(str(path)+str(entry))] try: all_journals = find_journals('%s/webjournal/' % CFG_ETCDIR) except: all_journals = [] mail_msg = _("Contact %(x_url_open)sthe administrator%(x_url_close)s") % \ {'x_url_open' : '<a href="mailto:%s">' % CFG_SITE_SUPPORT_EMAIL, 'x_url_close' : '</a>'} box = ''' <div style="text-align: center;"> <fieldset style="width:400px; margin-left: auto; margin-right:auto"> <legend style="color:#a70509;background-color:#fff;"> <i>%s</i> </legend> <p style="text-align:center;">%s</p> <h2 style="color:#0D2B88;">%s</h2> <ul class="webjournalBoxList"> %s </ul> <br/> <div style="text-align:right;"> %s </div> </fieldset> </div> ''' % (box_title, box_text, box_list_title, "".join(['<li><a href="%s/journal/?name=%s">%s</a></li>' % (CFG_SITE_URL, journal, journal) for journal in all_journals]), mail_msg) return page(title=title, body=box) def tmpl_webjournal_error_box(self, ln, title, title_msg, msg): """ returns an error box for webjournal errors. """ _ = gettext_set_language(ln) title = _(title) title_msg = _(title_msg) msg = _(msg) mail_msg = _("Contact %(x_url_open)sthe administrator%(x_url_close)s") % \ {'x_url_open' : '<a href="mailto:%s">' % CFG_SITE_SUPPORT_EMAIL, 'x_url_close' : '</a>'} box = ''' <div style="text-align: center;"> <fieldset style="width:400px; margin-left: auto; margin-right: auto;"> <legend style="color:#a70509;background-color:#fff;"> <i>%s</i> </legend> <p style="text-align:center;">%s</p> <br/> <div style="text-align:right;"> %s </div> </fieldset> </div> ''' % (title_msg, msg, mail_msg) return page(title=title, body=box) def tmpl_admin_regenerate_success(self, ln, journal_name, issue): """ Success message if a user applied the "regenerate" link. Links back to the regenerated journal. """ _ = gettext_set_language(ln) out = ''' The issue number %(issue)s for the %(journal_name)s journal has been successfully regenerated. <br/> Look at your changes: >> <a href="%(CFG_SITE_URL)s/journal/%(journal_name)s/%(issue_year)s/%(issue_number)s"> %(journal_name)s </a> <br/> or go back to this journal <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>. ''' % {'issue': issue, 'journal_name': journal_name, 'CFG_SITE_URL': CFG_SITE_URL, 'issue_year': issue.split('/')[1], 'issue_number': issue.split('/')[0]} return out def tmpl_admin_regenerate_error(self, ln, journal_name, issue): """ Failure message for a regeneration try. """ _ = gettext_set_language(ln) return page( title=_("Regeneration Error"), body = _("The issue could not be correctly regenerated. " "Please contact your administrator.")) def tmpl_admin_feature_record(self, journal_name, featured_records=[], ln=CFG_SITE_LANG, msg=None): """ Display an interface form to feature a specific record from CDS Invenio. """ _ = gettext_set_language(ln) out = '' out += '''<table class="admin_wvar"> <tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr> <tr> <td>0. <small><a href="administrate?journal_name=%(journal_name)s">Administrate</a></small> </td> <td>1. <small>Feature a Record</small> </td> <td>2. <small><a href="configure?action=edit&journal_name=%(journal_name)s">Edit Configuration</a></small> </td> <td>3. <small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small> </td> </tr> </table><br/>''' % {'journal_name': journal_name, 'menu': _("Menu"), 'CFG_SITE_URL': CFG_SITE_URL} if msg is not None: out += msg out += '<br/><br/>' out += '''<table class="admin_wvar" cellspacing="0" width="400px"> <tr> <th colspan="3" class="adminheader">Featured records</th> </tr>''' color = "fff" for (recid, img_url) in featured_records: out += '''<tr style="background-color:#%(color)s"> <td class="admintd"><img src="%(img_url)s" alt="" height="40px"/></td> <td class="admintdleft"><a href="%(CFG_SITE_URL)s/record/%(recid)s">Record %(recid)s</a></td> <td class="admintdright"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/feature_record?journal_name=%(journal_name)s&action=askremove&recid=%(recid)s">remove</a></td> </tr>''' % {'color': color, 'journal_name': journal_name, 'recid': recid, 'img_url': img_url, 'CFG_SITE_URL': CFG_SITE_URL} if color == 'fff': color = 'EBF7FF' else: color = 'fff' if len(featured_records) == 0: out += '<tr><td colspan="3" class="admintd"><em>No record featured for the moment. Add one using the form below.</em></td></tr>' out += '</table>' out += ''' <br/><br/><br/> <form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/feature_record" method="post"> <input type="hidden" name="action" value="add" /> <input type="hidden" name="journal_name" value="%(journal_name)s"/> <table class="admin_wvar" cellspacing="0"> <tr> <th colspan="2" class="adminheaderleft">Add a new featured record:</th> </tr> <tr> <td class="admintdright"><label for="recordid"><span style="white-space: nowrap;">Featured Record ID</span></label>: </td> <td><input tabindex="1" type="text" name="recid" value="" id="recordid"/></td> </tr> <tr> <td class="admintdright"><label for="image_url"><span style="white-space: nowrap;">Icon URL</span></label>: </td> <td><input tabindex="2" type="text" name="img_url" value="" id="image_url" size="60"/><em><br/><small>Image displayed along the featured record</small></em></td> </tr> <tr> <td colspan="2" align="right"><input tabindex="3" class="adminbutton" type="submit" value="Add"/></td> </tr> </table> </form> ''' % {'CFG_SITE_URL': CFG_SITE_URL, 'journal_name': journal_name} return out def tmpl_admin_alert_plain_text(self, journal_name, ln, issue): """ Default plain text message for email alert of journal updates. This will be used to pre-fill the content of the mail alert, that can be modified by the admin. Customize this function to return different default texts based on journal name and language, """ current_publication = get_issue_number_display(issue, journal_name, ln) plain_text = u'''Dear Subscriber, The latest issue of %(journal_name)s, no. %(current_publication)s, has been released. You can access it at the following URL: %(CFG_SITE_URL)s/journal/%(journal_name)s/ Best Wishes, %(journal_name)s team ---- Cher Abonné, Le nouveau numéro de %(journal_name)s, no. %(current_publication)s, vient de paraître. Vous pouvez y accéder à cette adresse : %(CFG_SITE_URL)s/journal/%(journal_name)s/?ln=fr Bonne lecture, L'équipe de %(journal_name)s ''' % {'journal_name': journal_name, 'current_publication': current_publication, 'CFG_SITE_URL': CFG_SITE_URL} return plain_text # ' + def tmpl_admin_alert_header_html(self, journal_name, ln, issue): + """ + Returns HTML header to be inserted into the HTML alert + + @param journal_name: the journal name + @param ln: the current language + @param issue: the issue for wich the alert is sent + """ + _ = gettext_set_language(ln) + journal_url = '%(CFG_SITE_URL)s/journal/%(journal_name)s/%(year)s/%(number)s' % \ + {'CFG_SITE_URL': CFG_SITE_URL, + 'journal_name': journal_name, + 'year': issue.split('/')[1], + 'number': issue.split('/')[0]} + journal_link = '<a href="%(journal_url)s">%(journal_url)s</a>' % \ + {'journal_url': journal_url} + return '<p class="htmlalertheader">' + \ + _('If you cannot read this email please go to %(x_journal_link)s') % {'x_journal_link': journal_link} + \ + '</p>' + def tmpl_admin_alert_subject(self, journal_name, ln, issue): """ Default subject for email alert of journal updates. Customize this function to return different default texts based on journal name and language, """ return "%s %s released" % (journal_name, \ get_issue_number_display(issue, journal_name, ln)) def tmpl_admin_alert_interface(self, ln, journal_name, default_subject, default_msg, default_recipients, alert_ln): """ Alert email interface. """ _ = gettext_set_language(ln) interface = ''' <table> <tr> <td valign="top"> <form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/alert" name="alert" method="post"> <input type="hidden" name="journal_name" value="%(journal_name)s"/> <p>Recipients:</p> <input type="text" name="recipients" value="%(default_recipients)s" size="60" /> <p>Subject:</p> <input type="text" name="subject" value="%(subject)s" size="60" /> <p>Plain Text Message:</p> <textarea name="plainText" wrap="soft" rows="25" cols="80">%(plain_text)s</textarea> <p> <input type="checkbox" name="htmlMail" id="htmlMail" value="html" checked="checked" /> <label for="htmlMail">Send journal front-page <small>(<em>HTML newsletter</em>)</small></label> </p> <br/> <input class="formbutton" type="submit" value="Send Alert" name="sent"/> </form> </td><td valign="top"> <p>HTML newsletter preview:</p> <iframe id="htmlMailPreview" src="%(CFG_SITE_URL)s/journal/%(journal_name)s?ln=%(alert_ln)s" height="600" width="600"></iframe> </tr> </table> ''' % {'CFG_SITE_URL': CFG_SITE_URL, 'journal_name': journal_name, 'subject': default_subject, 'plain_text': default_msg, 'default_recipients': default_recipients, 'alert_ln': alert_ln} return interface def tmpl_admin_alert_was_already_sent(self, ln, journal_name, subject, plain_text, recipients, html_mail, issue): """ """ _ = gettext_set_language(ln) out = ''' <form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/alert" name="alert" method="post"> <input type="hidden" name="journal_name" value="%(journal_name)s"/> <input type="hidden" name="recipients" value="%(recipients)s" /> <input type="hidden" name="subject" value="%(subject)s" /> <input type="hidden" name="plainText" value="%(plain_text)s" /> <input type="hidden" name="htmlMail" value="%(html_mail)s" /> <input type="hidden" name="force" value="True" /> <p><em>WARNING! </em>The email alert for the issue %(issue)s has already been - sent. Are you absolutely sure you want to it send it again?</p> + sent. Are you absolutely sure you want to send it again?</p> <p>Maybe you forgot to release an update issue? If so, please do this first <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/issue_control?journal_name=%(journal_name)s&issue=%(issue)s">here</a>.</p> <p>Proceed with caution, or your subscribers will receive the alert a second time.</p> <br/> <input class="formbutton" type="submit" value="I really want this!" name="sent"/> </form> ''' % {'CFG_SITE_URL': CFG_SITE_URL, 'journal_name': journal_name, 'recipients': recipients, 'subject': subject, 'plain_text': plain_text, 'html_mail': html_mail, 'issue': issue} return out def tmpl_admin_alert_unreleased_issue(self, ln, journal_name): """ Tried to announce an unreleased issue """ _ = gettext_set_language(ln) out = '''<p style="color:#f00">An alert cannot be send for this issue!</p> You tried to send an alert for an issue that has not yet been released. Release it first and retry.<br/> Go back to the <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>. ''' % {'CFG_SITE_URL': CFG_SITE_URL, 'journal_name': journal_name} return out def tmpl_admin_alert_success_msg(self, ln, journal_name): """ Success messge for the alert system. """ _ = gettext_set_language(ln) out = '''<p style="color:#0f0">Alert sent successfully!</p> Return to your journal here: >> \ <a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">%(journal_name)s</a> <br/> or go back to the <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>''' % {'CFG_SITE_URL': CFG_SITE_URL, 'journal_name': journal_name} return out def tmpl_admin_control_issue(self, ln, journal_name, active_issues): """ Display the interface allowing to set the current issue. """ _ = gettext_set_language(ln) out = ''' <p>This interface gives you the possibility to create your current webjournal publication. Every checked issue number will be in the current publication. Once you have made your selection you can publish the new issue by clicking the %(publish)s button at the end. </p> <form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/issue_control" name="publish"> <input type="hidden" name="journal_name" value="%(journal_name)s"/> Issue Numbers to publish: <ul> %(issues_list)s </ul> <br/> <p>Add a higher issue number by clicking "%(add)s"</p> <input class="formbutton" type="submit" value="%(add)s" name="action"/> <p>.. or add a custom issue number by typing it here and pressing "%(refresh)s"</p> <input type="text" value="ww/YYYY" name="issue"/> <input class="formbutton" type="submit" value="%(refresh)s" name="action"/> <br/> <br/> <p>If all issues you want to publish are correctly checked, proceed \ by clicking "%(publish)s".</p> <input class="formbutton" type="submit" value="%(publish)s" name="action"/> </form> ''' % {'CFG_SITE_URL': CFG_SITE_URL, 'journal_name': journal_name, 'issues_list': "".join(['<li><input type="checkbox" name="issue" value="%s" CHECKED> %s</input></li>' % (issue, issue) for issue in active_issues]), 'add' : _("Add"), 'publish' : _("Publish"), 'refresh' : _("Refresh") } return out def tmpl_admin_control_issue_success_msg(self, ln, active_issues, journal_name): """ An issue was successfully published """ _ = gettext_set_language(ln) issue_string = "".join([" - %s" % issue for issue in active_issues]) title = '<h2>Issue(s) %s created successfully!</h2>' % issue_string body = '''<p>Now you can:</p> <p>Return to your journal here: >> <a href="%s/journal/%s"> %s </a> </p> <p>Make additional publications here: >> <a href="%s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%s">Publishing Interface</a> </p> <p>Send an alert email here: >> <a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s"> Send an alert</a> </p>''' % (CFG_SITE_URL, journal_name, journal_name, CFG_SITE_URL, journal_name, CFG_SITE_URL, journal_name) return title + body def tmpl_admin_update_issue(self, ln, journal_name, next_issue, current_issue): """ A form that lets a user make an update to an issue number. """ _ = gettext_set_language(ln) current_articles = get_number_of_articles_for_issue(current_issue, journal_name, ln) next_articles = get_number_of_articles_for_issue(next_issue, journal_name, ln) html = ''' <p>The Issue that was released on week %(current_issue)s has pending updates scheduled. The next update for this issue is %(next_issue)s.</p> <p><em>Note: If you want to make a new release, please click through all the pending updates first.</em></p> <p>Do you want to release the update from issue: <br/> <em>%(current_issue)s</em> (%(current_articles)s) <br/> to issue: <br/> <em>%(next_issue)s</em> (%(next_articles)s) <br/> now?</p> <form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/issue_control" name="publish"> <input type="hidden" name="journal_name" value="%(journal_name)s"/> <input type="hidden" name="issue" value="%(next_issue)s"/> <input class="formbutton" type="submit" value="%(update)s" name="action"/> </form> ''' % {'current_issue': current_issue, 'next_issue' : next_issue, 'current_articles': ",".join(["%s : %s" % (item[0], item[1]) \ for item in current_articles.iteritems()]), 'next_articles': ",".join(["%s : %s" % (item[0], item[1]) \ for item in next_articles.iteritems()]), 'CFG_SITE_URL' : CFG_SITE_URL, 'journal_name': journal_name, 'update': _("Update")} return html def tmpl_admin_updated_issue_msg(self, ln, update_issue, journal_name): """ Prints a success message for the Update release of a journal. """ _ = gettext_set_language(ln) title = '<h2>Journal update %s published successfully!</h2>' % update_issue body = '''<p>Now you can:</p> <p>Return to your journal here: >> <a href="%s/journal/%s"> %s </a> </p> <p>Go back to the publishing interface: >> <a href="%s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%s">Issue Interface</a> </p> <p>Send an alert email here: >> <a href="%s/journal/alert?name=%s"> Send an alert</a> </p>''' % (CFG_SITE_URL, journal_name, journal_name, CFG_SITE_URL, journal_name, CFG_SITE_URL, journal_name) return title + body def tmpl_admin_administrate(self, journal_name, current_issue, current_publication, issue_list, next_issue_number, ln=CFG_SITE_LANG, as_editor=True): """ Returns an administration interface that shows the current publication and supports links to all important actions. @param as_editor: True if can make changes to the configuration. Else read-only mode. """ _ = gettext_set_language(ln) out = '' if as_editor: admin_menu = '''<table class="admin_wvar"> <tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr> <tr> <td>0. <small>Administrate</small> </td> <td>1. <small><a href="feature_record?journal_name=%(journal_name)s">Feature a Record</a></small> </td> <td>2. <small><a href="configure?action=edit&journal_name=%(journal_name)s">Edit Configuration</a></small> </td> <td>3. <small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small> </td> </tr> </table><br/>''' else: admin_menu = '''<table class="admin_wvar"> <tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr> <tr> <td>0. <small>Administrate</small> </td> <td>1. <small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small> </td> </tr> </table><br/>''' out += admin_menu % {'journal_name': journal_name, 'menu': _("Menu"), 'CFG_SITE_URL': CFG_SITE_URL} # format the issues issue_boxes = [] issue_list.append(next_issue_number) for issue in issue_list: articles = get_number_of_articles_for_issue(issue, journal_name, ln) released_on = get_release_datetime(issue, journal_name, ln) announced_on = get_announcement_datetime(issue, journal_name, ln) issue_box = ''' <tr style="%s"> <td class="admintdright" style="vertical-align: middle;"></td> <td class="admintdleft" style="white-space: nowrap; vertical-align: middle;"> <p>Issue: %s</p> <p>Publication: %s</p> </td> <td class="admintdright" style="vertical-align: middle;"> %s </td> <td class="admintdright" style="vertical-align: middle;"> <p>%s</p> <p>%s</p> </td> <td class="admintdright" style="vertical-align: middle;"> <p><a href="%s/admin/webjournal/webjournaladmin.py/regenerate?journal_name=%s&issue=%s&ln=%s">>regenerate</a></p> </td> <tr> ''' % ((issue==current_issue) and "background:#00FF00;" or "background:#F1F1F1;", issue, (issue==next_issue_number) and "?" or current_publication, "\n".join(['<p>%s : %s <a href="%s/journal/%s/%s/%s/%s">>view</a></p>' % (item[0], item[1], CFG_SITE_URL, journal_name, issue.split('/')[1], issue.split('/')[0], item[0]) \ for item in articles.iteritems()]), (not released_on) and ('<em>not released</em>' + (as_editor and '<br/><a href="%s/admin/webjournal/webjournaladmin.py/issue_control?journal_name=%s">>release now</a>' % (CFG_SITE_URL, journal_name) or '')) or 'released on: %s' % released_on.strftime("%d.%m.%Y"), (not announced_on) and ('<em>not announced</em>' + (as_editor and '<br/><a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s&issue=%s">>announce now</a>' % (CFG_SITE_URL, journal_name, issue) or '')) or 'announced on: %s <br/><a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s&issue=%s">>re-announce</a>' % (announced_on.strftime("%d.%m.%Y"), CFG_SITE_URL, journal_name, issue), CFG_SITE_URL, journal_name, issue, ln ) issue_boxes.append(issue_box) out += ''' <table class="admin_wvar" width="80%%" cellspacing="0"> <tbody> <tr> <th class="adminheaderleft"></th> <th class="adminheaderleft">Issue / Publication</th> <th class="adminheader">Articles</th> <th class="adminheaderleft">Release / Announcement</th> <th class="adminheaderleft">Cache Status</th> <tr> %s </tbody> </table> ''' % ("\n".join([issue_box for issue_box in issue_boxes])) return out def tmpl_admin_index(self, ln, journals, msg=None): """ Returns the admin index page content. Lists the journals, and offers options to edit them, delete them or add new journal params: ln - ln journals - list of tuples (journal_info dict, as_editor) msg - message to be displayed """ out = "" if msg is not None: out += msg out += ''' <p>Choose the journal you want to administrate.</p> <table class="admin_wvar" cellspacing="0"> <tr> <th class="adminheader">Journals</th> <th colspan="2" class="adminheader"> </th> </tr> ''' color = "fff" for journal_info, as_editor in journals: row = '''<tr style="background-color:#%(color)s"> <td class="admintdleft"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">%(journal_name)s</a></td> <td class="admintdright"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">edit</a></td>''' if as_editor: row += '<td class="admintdright"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/index?journal_name=%(journal_name)s&action=askDelete">delete</a></td>' row += '</tr>' out += row % {'color': color, 'journal_name': journal_info['journal_name'], 'journal_id': journal_info['journal_id'], 'CFG_SITE_URL': CFG_SITE_URL} if color == 'fff': color = 'EBF7FF' else: color = 'fff' out += '''<tr style="background-color:#%(color)s"> <td class="admintdleft" colspan="3" style="padding: 5px 10px;"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/configure?action=add">Add new journal</a></td> </tr>''' % {'color': color, 'CFG_SITE_URL': CFG_SITE_URL} out += '</table>' return out def tmpl_admin_configure_journal(self, ln, journal_name='', xml_config=None, action='edit', msg=None): """ Display a page to change the settings of a journal. Also used to add a new journal. """ out = '' _ = gettext_set_language(ln) journal_name_readonly = 'readonly="readonly" disabled="disabled"' journal_name_note = '' submit_button_label = _('Apply') if action == 'add': journal_name = '' journal_name_readonly = '' journal_name_note = 'Used in URLs. Choose it short and meaningful. This cannot be changed later' submit_button_label = _('Add') elif action in ['edit', 'editDone']: # Display navigation menu out += '''<table class="admin_wvar"> <tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr> <tr> <td>0. <small><a href="administrate?journal_name=%(journal_name)s">Administrate</a></small> </td> <td>1. <small><a href="feature_record?journal_name=%(journal_name)s">Feature a Record</a></small> </td> <td>2. <small>Edit Configuration</small> </td> <td>3. <small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small> </td> </tr> </table><br/>''' % {'journal_name': journal_name, 'menu': _("Menu"), 'CFG_SITE_URL': CFG_SITE_URL} if msg is not None: out += msg out += '<br/><br/>' out += ''' <form action="configure" method="post"> <input type="hidden" name="ln" value="%(ln)s" /> <input type="hidden" name="action" value="addDone" /> <table class="admin_wvar" cellspacing="0" style="width:90%%"> <tr> <th colspan="2" class="adminheaderleft"> Journal settings</th> </tr> <tr> <td class="admintdright" width="100px"><label for="journal_name">Name</label>: </td> <td><input tabindex="0" name="journal_name" type="text" id="journal_name" maxlength="50" size="15" value="%(journal_name)s" %(readonly)s %(journal_name_readonly)s /><small>%(journal_name_note)s</small></td> </tr> <tr> <td class="admintdright"><label for="xml_config">Config</label>: </td> <td><textarea wrap="soft" rows="25" style="width:100%%" tabindex="3" name="xml_config" id="xml_config" size="25" %(readonly)s>%(xml_config)s</textarea></td> </tr> <td colspan="2" align="right"><input type="submit" class="adminbutton" value="%(submit_button_label)s"></td> </tr> </table> </form> ''' % {'journal_name': journal_name, 'ln': ln, 'readonly': '', 'disabled': '', 'xml_config': xml_config.encode('utf-8'), 'journal_name_note': journal_name_note, 'submit_button_label': submit_button_label, 'journal_name_readonly': journal_name_readonly} return out diff --git a/modules/webjournal/lib/webjournaladminlib.py b/modules/webjournal/lib/webjournaladminlib.py index 1709b07bd..ec49a5bf5 100644 --- a/modules/webjournal/lib/webjournaladminlib.py +++ b/modules/webjournal/lib/webjournaladminlib.py @@ -1,886 +1,903 @@ ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # pylint: disable-msg=C0301 """CDS Invenio WebJournal Administration Interface.""" __revision__ = "$Id$" import sys import cPickle import re import os from urllib2 import urlopen if sys.hexversion < 0x2040000: # pylint: disable-msg=W0622 from sets import Set as set # pylint: enable-msg=W0622 from invenio.errorlib import register_exception from invenio.config import \ CFG_SITE_URL, \ CFG_SITE_LANG, \ CFG_SITE_NAME, \ CFG_ETCDIR, \ CFG_CACHEDIR, \ CFG_TMPDIR, \ CFG_SITE_SUPPORT_EMAIL from invenio.messages import gettext_set_language from invenio.mailutils import send_email from invenio.access_control_engine import acc_authorize_action from invenio.webjournal_config import \ InvenioWebJournalJournalIdNotFoundDBError, \ InvenioWebJournalReleaseUpdateError, \ InvenioWebJournalNoJournalOnServerError from invenio.webjournal_utils import \ get_journals_ids_and_names, \ guess_journal_name, \ get_current_issue, \ get_issue_number_display, \ get_featured_records, \ add_featured_record, \ remove_featured_record, \ clear_cache_for_issue, \ get_next_journal_issues, \ get_release_datetime, \ get_journal_id, \ compare_issues, \ get_journal_info_path, \ get_journal_css_url, \ get_journal_alert_sender_email, \ get_journal_alert_recipient_email, \ get_journal_draft_keyword_to_remove, \ get_journal_categories, \ get_journal_articles, \ get_grouped_issues, \ get_journal_issue_grouping, \ get_journal_languages from invenio.dbquery import run_sql from invenio.bibrecord import \ create_record, \ print_rec from invenio.bibformat import format_record from invenio.bibtask import task_low_level_submission from invenio.webjournal_config import \ InvenioWebJournalNoJournalOnServerError import invenio.template wjt = invenio.template.load('webjournal') 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 perform_index(ln=CFG_SITE_LANG, journal_name=None, action=None, uid=None): """ Index page Lists the journals, and offers options to edit them, delete them or add new journal. Parameters: journal_name - the journal affected by action, if any action - one of ['', 'askDelete', _('Delete'), _('Cancel')] ln - language uid - user id """ _ = gettext_set_language(ln) msg = None if action == 'askDelete' and journal_name is not None: msg = '''<fieldset style="display:inline;margin-left:auto;margin-right:auto;"> <legend>Delete Journal Configuration</legend><span style="color:#f00">Are you sure you want to delete the configuration of %(journal_name)s? <form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py"> <input type="hidden" name="journal_name" value="%(journal_name)s" /> <input class="formbutton" type="submit" name="action" value="%(delete)s" /> <input class="formbutton" type="submit" name="action" value="%(cancel)s" /> </form></span></fieldset>''' % {'CFG_SITE_URL': CFG_SITE_URL, 'journal_name': journal_name, 'delete': _("Delete"), 'cancel': _("Cancel")} if action == _("Delete") and journal_name is not None: # User confirmed and clicked on "Delete" button remove_journal(journal_name) journals = get_journals_ids_and_names() # Only keep journal that user can view or edit journals = [(journal_info, acc_authorize_action(uid, 'cfgwebjournal', name=journal_info['journal_name'], with_editor_rights='yes')[0] == 0) \ for journal_info in journals \ if acc_authorize_action(uid, 'cfgwebjournal', name=journal_info['journal_name'])[0] == 0] return wjt.tmpl_admin_index(ln=ln, journals=journals, msg=msg) def perform_administrate(ln=CFG_SITE_LANG, journal_name=None, as_editor=True): """ Administration of a journal Show the current and next issues/publications, and display links to more specific administrative pages. Parameters: journal_name - the journal to be administrated ln - language with_editor_rights - True if can edit configuration. Read-only mode otherwise """ if journal_name is None: try: journal_name = guess_journal_name(ln) except InvenioWebJournalNoJournalOnServerError, e: return e.user_box() if not can_read_xml_config(journal_name): return '<span style="color:#f00">Configuration could not be read. Please check that %s/webjournal/%s/%s-config.xml exists and can be read by the server.</span><br/>' % (CFG_ETCDIR, journal_name, journal_name) current_issue = get_current_issue(ln, journal_name) current_publication = get_issue_number_display(current_issue, journal_name, ln) issue_list = get_grouped_issues(journal_name, current_issue) next_issue_number = get_next_journal_issues(issue_list[-1], journal_name, 1) return wjt.tmpl_admin_administrate(journal_name, current_issue, current_publication, issue_list, next_issue_number[0], ln, as_editor=as_editor) def perform_feature_record(journal_name, recid, img_url='', action='', ln=CFG_SITE_LANG): """ Interface to feature a record Used to list, add and remove featured records of the journal. Parameters: journal_name - the journal for which the article is featured recid - the record affected by 'action' img_url - the URL to image displayed with given record (only when action == 'add') action - One of ['', 'add', 'askremove', _('Remove'), _('Cancel')] ln - language """ _ = gettext_set_language(ln) if action == 'add': result = add_featured_record(journal_name, recid, img_url) if result == 0: msg ='''<span style="color:#0f0">Successfully featured <a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a>. Go to the <a href="%(CFG_SITE_URL)s/journal/%(name)s">%(name)s journal</a> to see the result.</span>''' % {'CFG_SITE_URL': CFG_SITE_URL, 'name': journal_name, 'recid': recid} elif result == 1: msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a> is already featured. Choose another one or remove it first.</span>''' % \ {'CFG_SITE_URL': CFG_SITE_URL, 'recid': recid} else: msg = '''<span style="color:#f00">Record could not be featured. Check file permission.</span>''' featured_records = get_featured_records(journal_name) return wjt.tmpl_admin_feature_record(ln=ln, journal_name=journal_name, featured_records=featured_records, msg=msg) elif action == 'askremove': msg = '''<fieldset style="display:inline;margin-left:auto;margin-right:auto;"> <legend>Remove featured record</legend><span style="color:#f00">Are you sure you want to remove <a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a> from the list of featured record? <form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/feature_record"> <input type="hidden" name="journal_name" value="%(name)s" /> <input type="hidden" name="recid" value="%(recid)s" /> <input class="formbutton" type="submit" name="action" value="%(remove)s" /> <input class="formbutton" type="submit" name="action" value="%(cancel)s" /> </form></span></fieldset>''' % \ {'CFG_SITE_URL': CFG_SITE_URL, 'name': journal_name, 'recid': recid, 'cancel': _("Cancel"), 'remove': _("Remove")} featured_records = get_featured_records(journal_name) return wjt.tmpl_admin_feature_record(ln=ln, journal_name=journal_name, featured_records=featured_records, msg=msg) elif action == _("Remove"): result = remove_featured_record(journal_name, recid) msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/record/%(recid)s">Record %(recid)s</a> has been removed.</span>''' % \ {'CFG_SITE_URL': CFG_SITE_URL, 'recid': recid} featured_records = get_featured_records(journal_name) return wjt.tmpl_admin_feature_record(ln=ln, journal_name=journal_name, featured_records=featured_records, msg=msg) else: msg = '''Here you can choose which records from the %s should be featured on the journal webpage.''' % CFG_SITE_NAME featured_records = get_featured_records(journal_name) return wjt.tmpl_admin_feature_record(ln=ln, journal_name=journal_name, featured_records=featured_records, msg=msg) def perform_regenerate_issue(issue, journal_name, ln=CFG_SITE_LANG): """ Clears the cache for the given issue. Parameters: journal_name - the journal for which the cache should be deleted issue - the issue for which the cache should be deleted ln - language """ success = clear_cache_for_issue(journal_name, issue) if success: return wjt.tmpl_admin_regenerate_success(ln, journal_name, issue) else: return wjt.tmpl_admin_regenerate_error(ln, journal_name, issue) def perform_request_issue_control(journal_name, issues, action, ln=CFG_SITE_LANG): """ Central logic for issue control. Regenerates the flat files 'current_issue' and 'issue_group' of the journal that control which issue is currently active for the journal. Parameters: journal_name - the journal affected by 'action' issues - list of issues affected by 'action' TODO: check action - One of ['cfg', _('Add'), _('Refresh'), _('Publish'), _('Update')] ln - language """ _ = gettext_set_language(ln) out = '' if action == "cfg" or action == _("Refresh") or action == _("Add"): # find out if we are in update or release current_issue = get_current_issue(ln, journal_name) grouped_issues = get_grouped_issues(journal_name, current_issue) if current_issue != grouped_issues[-1]: # The current issue has "pending updates", i.e. is grouped # with unreleased issues. Propose to update these issues next_issue = grouped_issues[grouped_issues.index(current_issue) + 1] out = wjt.tmpl_admin_update_issue(ln, journal_name, next_issue, current_issue) else: # Propose a release next_issues = get_next_journal_issues(current_issue, journal_name, n=get_journal_issue_grouping(journal_name)) if action == _("Refresh"): next_issues += issues next_issues = list(set(next_issues))# avoid double entries elif action == _("Add"): next_issues += issues next_issues = list(set(next_issues))# avoid double entries next_issues.sort(compare_issues) highest_issue_so_far = next_issues[-1] one_more_issue = get_next_journal_issues(highest_issue_so_far, journal_name, 1) next_issues += one_more_issue next_issues = list(set(next_issues)) # avoid double entries else: # get the next issue numbers to publish next_issues = get_next_journal_issues(current_issue, journal_name, n=get_journal_issue_grouping(journal_name)) next_issues.sort(compare_issues) out = wjt.tmpl_admin_control_issue(ln, journal_name, next_issues) elif action == _("Publish"): # Publish the given issues (mark them as current issues) publish_issues = issues publish_issues = list(set(publish_issues)) # avoid double entries publish_issues.sort(compare_issues) if len(publish_issues) == 0: # User did not select an issue current_issue = get_current_issue(ln, journal_name) next_issues = get_next_journal_issues(current_issue, journal_name, n=get_journal_issue_grouping(journal_name)) out = '<p style="color:#f00;text-align:center">' + \ _('Please select an issue') + '</p>' out += wjt.tmpl_admin_control_issue(ln, journal_name, next_issues) return out try: release_journal_issue(publish_issues, journal_name, ln) except InvenioWebJournalJournalIdNotFoundDBError, e: register_exception(req=None) return e.user_box() out = wjt.tmpl_admin_control_issue_success_msg(ln, publish_issues, journal_name) elif action == _("Update"): try: try: update_issue = issues[0] except: raise InvenioWebJournalReleaseUpdateError(ln, journal_name) except InvenioWebJournalReleaseUpdateError, e: register_exception(req=None) return e.user_box() try: release_journal_update(update_issue, journal_name, ln) except InvenioWebJournalJournalIdNotFoundDBError, e: register_exception(req=None) return e.user_box() out = wjt.tmpl_admin_updated_issue_msg(ln, update_issue, journal_name) return out def perform_request_alert(journal_name, issue, sent, plain_text, subject, recipients, html_mail, force, ln=CFG_SITE_LANG): """ All the logic for alert emails. Display a form to edit email/recipients and options to send the email. Sent in HTML/PlainText or only PlainText if wished so. Also prevent mistake of sending the alert more than one for a particular issue. Parameters: journal_name - the journal for which the alert is sent issue - the issue for which the alert is sent sent - Display interface to edit email if "False" (string). Else send the email. plain_text - the text of the mail subject - the subject of the mail recipients - the recipients of the mail (string with comma-separated emails) html_mail - if 'html', also send email as HTML (copying from the current issue on the web) force - if different than "False", the email is sent even if it has already been sent. ln - language """ # FIXME: more flexible options to choose the language of the alert languages = get_journal_languages(journal_name) if languages: alert_ln = languages[0] else: alert_ln = CFG_SITE_LANG if not get_release_datetime(issue, journal_name, ln): # Trying to send an alert for an unreleased issue return wjt.tmpl_admin_alert_unreleased_issue(ln, journal_name) if sent == "False": # Retrieve default message, subject and recipients, and # display email editor subject = wjt.tmpl_admin_alert_subject(journal_name, alert_ln, issue) plain_text = wjt.tmpl_admin_alert_plain_text(journal_name, alert_ln, issue) plain_text = plain_text.encode('utf-8') recipients = get_journal_alert_recipient_email(journal_name) return wjt.tmpl_admin_alert_interface(ln, journal_name, subject, plain_text, recipients, alert_ln) else: # User asked to send the mail if was_alert_sent_for_issue(issue, journal_name, ln) != False and force == "False": # Mmh, email already sent before for this issue. Ask # confirmation return wjt.tmpl_admin_alert_was_already_sent(ln, journal_name, subject, plain_text, recipients, html_mail, issue) html_string = None if html_mail == "html": # Also send as HTML: retrieve from current issue html_file = urlopen('%s/journal/%s?ln=%s' % (CFG_SITE_URL, journal_name, alert_ln)) html_string = html_file.read() html_file.close() html_string = put_css_in_file(html_string, journal_name) + html_string = insert_journal_link(html_string, journal_name, issue, ln) sender_email = get_journal_alert_sender_email(journal_name) send_email(sender_email, recipients, subject, plain_text, html_string, header='', footer='', html_header='', html_footer='', charset='utf-8') update_DB_for_alert(issue, journal_name, ln) return wjt.tmpl_admin_alert_success_msg(ln, journal_name) def perform_request_configure(journal_name, xml_config, action, ln=CFG_SITE_LANG): """ Add a new journal or configure the settings of an existing journal. Parameters: journal_name - the journal to configure, or name of the new journal xml_config - the xml configuration of the journal (string) action - One of ['edit', 'editDone', 'add', 'addDone'] ln - language """ msg = None if action == 'edit': # Read existing config if journal_name is not None: if not can_read_xml_config(journal_name): return '<span style="color:#f00">Configuration could not be read. Please check that %s/webjournal/%s/%s-config.xml exists and can be read by the server.</span><br/>' % (CFG_ETCDIR, journal_name, journal_name) config_path = '%s/webjournal/%s/%s-config.xml' % (CFG_ETCDIR, journal_name, journal_name) xml_config = file(config_path).read() else: # cannot edit unknown journal... return '<span style="color:#f00">You must specify a journal name</span>' if action in ['editDone', 'addDone']: # Save config if action == 'addDone': res = add_journal(journal_name, xml_config) if res == -1: msg = '<span style="color:#f00">A journal with that name already exists. Please choose another name.</span>' action = 'add' elif res == -2: msg = '<span style="color:#f00">Configuration could not be written (no permission). Please manually copy your config to %s/webjournal/%s/%s-config.xml</span><br/>' % (CFG_ETCDIR, journal_name, journal_name) action = 'edit' elif res == -4: msg = '<span style="color:#f00">Cache file could not be written (no permission). Please manually create directory %s/webjournal/%s/ and make it writable for your Apache user</span><br/>' % (CFG_CACHEDIR, journal_name) action = 'edit' elif res > 0: msg = '<span style="color:#0f0">Journal successfully added.</span>' action = 'edit' else: msg = '<span style="color:#f00">An error occurred. The journal could not be added</span>' action = 'edit' if action == 'add': # Display a sample config. xml_config = '''<?xml version="1.0" encoding="UTF-8"?> <webjournal name="AtlantisTimes"> <view> <niceName>Atlantis Times</niceName> <niceURL>%(CFG_SITE_URL)s</niceURL> <css> <screen>/img/AtlantisTimes.css</screen> <print>/img/AtlantisTimes.css</print> </css> <format_template> <index>AtlantisTimes_Index.bft</index> <detailed>AtlantisTimes_Detailed.bft</detailed> <search>AtlantisTimes_Search.bft</search> <popup>AtlantisTimes_Popup.bft</popup> <contact>AtlantisTimes_Contact.bft</contact> </format_template> </view> <model> <record> <rule>News, 980__a:ATLANTISTIMESNEWS or 980__a:ATLANTISTIMESNEWSDRAFT</rule> <rule>Science, 980__a:ATLANTISTIMESSCIENCE or 980__a:ATLANTISTIMESSCIENCEDRAFT</rule> <rule>Arts, 980__a:ATLANTISTIMESARTS or 980__a:ATLANTISTIMESARTSDRAFT</rule> </record> </model> <controller> <issue_grouping>2</issue_grouping> <issues_per_year>52</issues_per_year> <hide_unreleased_issues>all</hide_unreleased_issues> <marc_tags> <issue_number>773__n</issue_number> <order_number>773__c</order_number> </marc_tags> <alert_sender>%(CFG_SITE_SUPPORT_EMAIL)s</alert_sender> <alert_recipients>recipients@atlantis.atl</alert_recipients> <languages>en,fr</languages> <submission> <doctype>DEMOJRN</doctype> <report_number_field>DEMOJRN_RN</report_number_field> </submission> <first_issue>02/2009</first_issue> <draft_keyword>DRAFT</draft_keyword> </controller> </webjournal>''' % {'CFG_SITE_URL': CFG_SITE_URL, 'CFG_SITE_SUPPORT_EMAIL': CFG_SITE_SUPPORT_EMAIL} out = wjt.tmpl_admin_configure_journal(ln=ln, journal_name=journal_name, xml_config=xml_config, action=action, msg=msg) return out ######################## ADDING/REMOVING JOURNALS ############################### def add_journal(journal_name, xml_config): """ Add a new journal to the DB. Also create the configuration file Parameters: journal_name - the name (used in URLs) of the new journal xml_config - the xml configuration of the journal (string) Returns: the id of the journal if successfully added -1 if could not be added because journal name already exists -2 if config could not be saved -3 if could not be added for other reasons -4 if database cache could not be added """ try: get_journal_id(journal_name) except InvenioWebJournalJournalIdNotFoundDBError: # Perfect, journal does not exist res = run_sql("INSERT INTO jrnJOURNAL (name) VALUES(%s)", (journal_name,)) # Also save xml_config config_dir = '%s/webjournal/%s/' % (CFG_ETCDIR, journal_name) try: if not os.path.exists(config_dir): os.makedirs(config_dir) xml_config_file = file(config_dir + journal_name + '-config.xml', 'w') xml_config_file.write(xml_config) xml_config_file.close() except Exception: res = -2 # And save some info in file in case database is down journal_info_path = get_journal_info_path(journal_name) journal_info_dir = os.path.dirname(journal_info_path) if not os.path.exists(journal_info_dir): try: os.makedirs(journal_info_dir) except Exception: if res <= 0: res = -4 journal_info_file = open(journal_info_path, 'w') cPickle.dump({'journal_id': res, 'journal_name': journal_name, 'current_issue':'01/2000'}, journal_info_file) return res return -1 def remove_journal(journal_name): """ Remove a journal from the DB. Does not completely remove everything, in case it was an error from the editor.. Parameters: journal_name - the journal to remove Returns: the id of the journal if successfully removed or -1 if could not be removed because journal name does not exist or -2 if could not be removed for other reasons """ run_sql("DELETE FROM jrnJOURNAL WHERE name=%s", (journal_name,)) ######################## TIME / ISSUE FUNCTIONS ############################### def release_journal_issue(publish_issues, journal_name, ln=CFG_SITE_LANG): """ Releases a new issue. This sets the current issue in the database to 'publish_issues' for given 'journal_name' Parameters: journal_name - the journal for which we release a new issue publish_issues - the list of issues that will be considered as current (there can be several) ln - language """ journal_id = get_journal_id(journal_name, ln) if len(publish_issues) > 1: publish_issues.sort(compare_issues) low_bound = publish_issues[0] high_bound = publish_issues[-1] issue_display = '%s-%s/%s' % (low_bound.split("/")[0], high_bound.split("/")[0], high_bound.split("/")[1]) # remember convention: if we are going over a new year, take the higher else: issue_display = publish_issues[0] # produce the DB lines for publish_issue in publish_issues: move_drafts_articles_to_ready(journal_name, publish_issue) run_sql("INSERT INTO jrnISSUE (id_jrnJOURNAL, issue_number, issue_display) \ VALUES(%s, %s, %s)", (journal_id, publish_issue, issue_display)) # set first issue to published release_journal_update(publish_issues[0], journal_name, ln) # update information in file (in case DB is down) journal_info_path = get_journal_info_path(journal_name) journal_info_file = open(journal_info_path, 'w') cPickle.dump({'journal_id': journal_id, 'journal_name': journal_name, 'current_issue': get_current_issue(ln, journal_name)}, journal_info_file) def delete_journal_issue(issue, journal_name, ln=CFG_SITE_LANG): """ Deletes an issue from the DB. (Not currently used) """ journal_id = get_journal_id(journal_name, ln) run_sql("DELETE FROM jrnISSUE WHERE issue_number=%s \ AND id_jrnJOURNAL=%s",(issue, journal_id)) # update information in file (in case DB is down) journal_info_path = get_journal_info_path(journal_name) journal_info_file = open(journal_info_path, 'w') cPickle.dump({'journal_id': journal_id, 'journal_name': journal_name, 'current_issue': get_current_issue(ln, journal_name)}, journal_info_file) def was_alert_sent_for_issue(issue, journal_name, ln): """ Returns False if alert has not already been sent for given journal and issue, else returns time of last alert, as time tuple Parameters: journal_name - the journal for which we want to check last alert issue - the issue for which we want to check last alert ln - language Returns: time tuple or False. Eg: (2008, 4, 25, 7, 58, 37, 4, 116, -1) """ journal_id = get_journal_id(journal_name, ln) date_announced = run_sql("SELECT date_announced FROM jrnISSUE \ WHERE issue_number=%s \ AND id_jrnJOURNAL=%s", (issue, journal_id))[0][0] if date_announced == None: return False else: return date_announced.timetuple() def update_DB_for_alert(issue, journal_name, ln): """ Update the 'last sent alert' timestamp for the given journal and issue. Parameters: journal_name - the journal for which we want to update the time of last alert issue - the issue for which we want to update the time of last alert ln - language """ journal_id = get_journal_id(journal_name, ln) run_sql("UPDATE jrnISSUE set date_announced=NOW() \ WHERE issue_number=%s \ AND id_jrnJOURNAL=%s", (issue, journal_id)) def release_journal_update(update_issue, journal_name, ln=CFG_SITE_LANG): """ Releases an update to a journal. """ move_drafts_articles_to_ready(journal_name, update_issue) journal_id = get_journal_id(journal_name, ln) run_sql("UPDATE jrnISSUE set date_released=NOW() \ WHERE issue_number=%s \ AND id_jrnJOURNAL=%s", (update_issue, journal_id)) def move_drafts_articles_to_ready(journal_name, issue): """ Move draft articles to their final "collection". To do so we rely on the convention that an admin-chosen keyword must be removed from the metadata """ protected_datafields = ['100', '245', '246', '520', '590', '700'] keyword_to_remove = get_journal_draft_keyword_to_remove(journal_name) categories = get_journal_categories(journal_name, issue) for category in categories: articles = get_journal_articles(journal_name, issue, category) for order, recids in articles.iteritems(): for recid in recids: record_xml = format_record(recid, of='xm') if not record_xml: continue new_record_xml_path = os.path.join(CFG_TMPDIR, 'webjournal_publish_' + \ str(recid) + '.xml') if os.path.exists(new_record_xml_path): # Do not modify twice continue record_struc = create_record(record_xml) record = record_struc[0] new_record = update_draft_record_metadata(record, protected_datafields, keyword_to_remove) new_record_xml = print_rec(new_record) if new_record_xml.find(keyword_to_remove) >= 0: new_record_xml = new_record_xml.replace(keyword_to_remove, '') # Write to file new_record_xml_file = file(new_record_xml_path, 'w') new_record_xml_file.write(new_record_xml) new_record_xml_file.close() # Submit task_low_level_submission('bibupload', 'WebJournal', '-c', new_record_xml_path) def update_draft_record_metadata(record, protected_datafields, keyword_to_remove): """ Returns a new record with fields that should be modified in order for this draft record to be considered as 'ready': keep only controlfield 001 and non-protected fields that contains the 'keyword_to_remove' Parameters: record - a single recored (as BibRecord structure) protected_datafields - *list* tags that should not be part of the returned record keyword_to_remove - *str* keyword that should be considered when checking if a field should be part of the returned record. """ new_record = {} for tag, field in record.iteritems(): if tag in protected_datafields: continue elif not keyword_to_remove in str(field) and \ not tag == '001': continue else: # Keep new_record[tag] = field return new_record ######################## XML CONFIG ############################### def can_read_xml_config(journal_name): """ Check that configuration xml for given journal name is exists and can be read. """ config_path = '%s/webjournal/%s/%s-config.xml' % \ (CFG_ETCDIR, journal_name, journal_name) try: file(config_path).read() except IOError: return False return True ######################## EMAIL HELPER FUNCTIONS ############################### +def insert_journal_link(html_string, journal_name, issue, ln): + """ + Insert a warning regarding HTML formatting inside mail client and + link to journal page just after the body of the page. + + @param html_string: the HTML newsletter + @param journal_name: the journal name + @param issue: journal issue for which the alert is sent (in the form number/year) + @param ln: language + """ + def replace_body(match_obj): + "Replace body with itself + header message" + header = wjt.tmpl_admin_alert_header_html(journal_name, ln, issue) + return match_obj.group() + header + return re.sub('<body.*?>', replace_body, html_string, 1) + def put_css_in_file(html_message, journal_name): """ Retrieve the CSS of the journal and insert/inline it in the <head> section of the given html_message. (Used for HTML alert emails) Parameters: journal_name - the journal name html_message - the html message (string) in which the CSS should be inserted Returns: the HTML message with its CSS inlined """ css_path = get_journal_css_url(journal_name) if not css_path: return css_file = urlopen(css_path) css = css_file.read() css = make_full_paths_in_css(css, journal_name) html_parted = html_message.split("</head>") if len(html_parted) > 1: html = '%s<style type="text/css">%s</style></head>%s' % (html_parted[0], css, html_parted[1]) else: html_parted = html_message.split("<html>") if len(html_parted) > 1: html = '%s<html><head><style type="text/css">%s</style></head>%s' % (html_parted[0], css, html_parted[1]) else: return return html def make_full_paths_in_css(css, journal_name): """ Update the URLs in a CSS from relative to absolute URLs, so that the URLs are accessible from anywhere (Used for HTML alert emails) Parameters: journal_name - the journal name css - a cascading stylesheet (string) Returns: (str) the given css with relative paths converted to absolute paths """ url_pattern = re.compile('''url\(["']?\s*(?P<url>\S*)\s*["']?\)''', re.DOTALL) url_iter = url_pattern.finditer(css) rel_to_full_path = {} for url in url_iter: url_string = url.group("url") url_string = url_string.replace('"', "") url_string = url_string.replace("'", "") if url_string[:6] != "http://": rel_to_full_path[url_string] = '"%s/img/webjournal_%s/%s"' % \ (CFG_SITE_URL, journal_name, url_string) for url in rel_to_full_path.keys(): css = css.replace(url, rel_to_full_path[url]) return css