Fix datepicker arrows style on hover
[cds-indico.git] / indico / MaKaC / webinterface / rh / CFADisplay.py
blobc83ce5fd488419b6a68517937d13993684f727a4
1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
16 from flask import request
18 from textwrap import TextWrapper
20 from BTrees.IOBTree import IOBTree
21 from cStringIO import StringIO
23 import MaKaC.webinterface.urlHandlers as urlHandlers
24 import MaKaC.webinterface.mail as mail
25 import MaKaC.webinterface.pages.abstracts as abstracts
26 import MaKaC.review as review
27 from MaKaC.webinterface.rh.conferenceDisplay import RHConferenceBaseDisplay
28 from MaKaC.webinterface.rh.base import RHModificationBaseProtected
29 from indico.core.db import DBMgr
30 from MaKaC.review import AbstractStatusSubmitted, AbstractStatusAccepted
31 from MaKaC.PDFinterface.conference import AbstractToPDF, AbstractsToPDF
32 from MaKaC.errors import MaKaCError, NoReportError
33 import MaKaC.common.timezoneUtils as timezoneUtils
34 from MaKaC.i18n import _
35 from indico.util.i18n import i18nformat
36 from indico.web.flask.util import send_file
37 from MaKaC.webinterface.common.abstractDataWrapper import AbstractParam
38 from MaKaC.webinterface.rh.fileAccess import RHFileAccess
39 from MaKaC.webinterface.common.tools import cleanHTMLHeaderFilename
40 from MaKaC.PDFinterface.base import LatexRunner
43 class RHBaseCFA( RHConferenceBaseDisplay ):
45 def _processIfActive( self ):
46 """only override this method if the CFA must be activated for
47 carrying on the handler execution"""
48 return "cfa"
50 def _process( self ):
51 #if the CFA is not activated we show up a form informing about that.
52 # This must be done at RH level because there can be some RH not
53 # displaying pages.
54 cfaMgr = self._conf.getAbstractMgr()
55 if not cfaMgr.isActive() or not self._conf.hasEnabledSection("cfa"):
56 p = abstracts.WPCFAInactive( self, self._conf )
57 return p.display()
58 else:
59 return self._processIfActive()
62 class RHConferenceCFA( RHBaseCFA ):
63 _uh = urlHandlers.UHConferenceCFA
65 def _processIfActive( self ):
66 p = abstracts.WPConferenceCFA( self, self._target )
67 return p.display()
70 class RHAbstractSubmissionBase(RHBaseCFA):
72 def _checkProtection(self):
73 self._checkSessionUser()
74 RHBaseCFA._checkProtection(self)
76 def _processIfOpened(self):
77 """only override this method if the submission period must be opened
78 for the request handling"""
79 return "cfa opened"
81 def _processIfActive(self):
82 cfaMgr = self._conf.getAbstractMgr()
83 #if the user is in the autorized list, don't check period
84 if self._getUser() in cfaMgr.getAuthorizedSubmitterList():
85 return self._processIfOpened()
86 #if the submission period is not yet opened we show up a form informing
87 # about that.
88 if timezoneUtils.nowutc() < cfaMgr.getStartSubmissionDate():
89 #if the submission period is already closed we show up a form informing
90 # about that.
91 p = abstracts.WPCFANotYetOpened(self, self._conf)
92 return p.display()
93 elif timezoneUtils.nowutc() > cfaMgr.getEndSubmissionDate():
94 p = abstracts.WPCFAClosed(self, self._conf, False)
95 return p.display()
96 else:
97 return self._processIfOpened()
100 class _AbstractSubmissionNotification:
102 def __init__(self, abstract):
103 self._abstract = abstract
104 self._conf = self._abstract.getConference()
105 self._subject = _("Abstract submission confirmation (%s)") % self._conf.getTitle()
107 def getAttachments(self):
108 return []
110 def getSubject(self):
111 return self._subject
113 def setSubject(self, s):
114 self._subject = s
116 def getDestination(self):
117 return self._abstract.getSubmitter()
119 def getFromAddr(self):
120 return self._conf.getSupportInfo().getEmail(returnNoReply=True)
122 def getCCList(self):
123 return self._abstract.getOwner().getSubmissionNotification().getCCList()
125 def getToList(self):
126 return self._abstract.getOwner().getSubmissionNotification().getToList()
128 def getMsg(self):
129 primary_authors = []
130 for auth in self._abstract.getPrimaryAuthorList():
131 primary_authors.append("""%s (%s) <%s>""" % (auth.getFullName(), auth.getAffiliation(), auth.getEmail()))
132 co_authors = []
133 for auth in self._abstract.getCoAuthorList():
134 email = ""
135 if auth.getEmail() != "":
136 email = " <%s>" % auth.getEmail()
137 co_authors.append("""%s (%s)%s""" % (auth.getFullName(), auth.getAffiliation(), email))
138 speakers = []
139 for spk in self._abstract.getSpeakerList():
140 speakers.append(spk.getFullName())
141 tracks = []
142 for track in self._abstract.getTrackListSorted():
143 tracks.append("""%s""" % track.getTitle())
144 msg = [i18nformat("""_("Dear") %s,""") % self._abstract.getSubmitter().getStraightFullName()]
145 msg.append("")
146 msg.append(_("The submission of your abstract has been successfully processed."))
147 msg.append("")
148 msg.append(i18nformat("""_("Abstract submitted"):\n<%s>.""") % urlHandlers.UHUserAbstracts.getURL(self._conf))
149 msg.append("")
150 msg.append(i18nformat("""_("Status of your abstract"):\n<%s>.""") % urlHandlers.UHAbstractDisplay.getURL(self._abstract))
151 msg.append("")
152 msg.append(i18nformat("""_("See below a detailed summary of your submitted abstract"):"""))
153 msg.append("")
154 msg.append(i18nformat("""_("Conference"): %s""") % self._conf.getTitle())
155 msg.append("")
156 msg.append(i18nformat("""_("Submitted by"): %s""") % self._abstract.getSubmitter().getFullName())
157 msg.append("")
158 msg.append(i18nformat("""_("Submitted on"): %s""") % self._abstract.getSubmissionDate().strftime("%d %B %Y %H:%M"))
159 msg.append("")
160 msg.append(i18nformat("""_("Title"): %s""") % self._abstract.getTitle())
161 msg.append("")
162 for f in self._conf.getAbstractMgr().getAbstractFieldsMgr().getFields():
163 msg.append(f.getCaption())
164 msg.append(str(self._abstract.getField(f.getId())))
165 msg.append("")
166 msg.append(i18nformat("""_("Primary Authors"):"""))
167 msg += primary_authors
168 msg.append("")
169 msg.append(i18nformat("""_("Co-authors"):"""))
170 msg += co_authors
171 msg.append("")
172 msg.append(i18nformat("""_("Abstract presenters"):"""))
173 msg += speakers
174 msg.append("")
175 msg.append(i18nformat("""_("Track classification"):"""))
176 msg += tracks
177 msg.append("")
178 ctype = i18nformat("""--_("not specified")--""")
179 if self._abstract.getContribType() is not None:
180 ctype = self._abstract.getContribType().getName()
181 msg.append(i18nformat("""_("Presentation type"): %s""") % ctype)
182 msg.append("")
183 msg.append(i18nformat("""_("Comments"): %s""") % self._abstract.getComments())
184 msg.append("")
185 return "\n".join(msg)
187 def getBody(self):
188 msg = self.getMsg()
189 return i18nformat("""
190 _("The following email has been sent to %s"):
194 %s""") % (self.getDestination().getFullName(), msg)
197 class RHAbstractModificationAction(RHAbstractSubmissionBase, AbstractParam):
199 def __init__(self):
200 RHAbstractSubmissionBase.__init__(self)
201 AbstractParam.__init__(self)
203 def _checkParams( self, params ):
204 RHAbstractSubmissionBase._checkParams(self, params)
205 #if the user is not logged in we return immediately as this form needs
206 # the user to be logged in and therefore all the checking below is not
207 # necessary
208 if self._getUser() is None:
209 return
211 AbstractParam._checkParams(self, params, self._conf, request.content_length)
214 class RHAbstractSubmission( RHAbstractModificationAction ):
215 _uh = urlHandlers.UHAbstractSubmission
217 def _doValidate( self ):
218 #First, one must validate that the information is fine
219 errors = self._abstractData.check()
220 if errors:
221 p = abstracts.WPAbstractSubmission( self, self._target )
222 pars = self._abstractData.toDict()
223 pars["action"] = self._action
224 pars["attachments"] = []
225 return p.display( **pars )
226 #Then, we create the abstract object and set its data to the one
227 # received
228 cfaMgr = self._target.getAbstractMgr()
229 abstract = cfaMgr.newAbstract( self._getUser() )
230 self._abstractData.setAbstractData(abstract)
231 #The commit must be forced before sending the confirmation
232 DBMgr.getInstance().commit()
233 #Email confirmation about the submission
234 mail.Mailer.send( _AbstractSubmissionNotification( abstract ), self._conf.getSupportInfo().getEmail(returnNoReply=True) )
235 #Email confirmation about the submission to coordinators
236 if cfaMgr.getSubmissionNotification().hasDestination():
237 asn=_AbstractSubmissionNotification( abstract )
238 asn.setSubject(_("[Indico] New abstract submission: %s")%asn.getDestination().getFullName())
239 mail.GenericMailer.send( asn )
240 #We must perform some actions: email warning to the authors
241 #Finally, we display a confirmation form
242 self._redirect( urlHandlers.UHAbstractSubmissionConfirmation.getURL( abstract ) )
244 def _processIfOpened( self ):
245 if self._action == "CANCEL":
246 self._redirect( urlHandlers.UHConferenceCFA.getURL( self._conf ) )
247 elif self._action == "VALIDATE":
248 return self._doValidate()
249 else:
250 p = abstracts.WPAbstractSubmission( self, self._target )
251 pars = self._abstractData.toDict()
252 return p.display( **pars )
255 class RHAbstractModify(RHAbstractModificationAction, RHModificationBaseProtected):
256 _uh = urlHandlers.UHAbstractModify
258 def _checkProtection(self):
259 RHModificationBaseProtected._checkProtection(self)
261 def _checkParams(self, params):
262 RHAbstractModificationAction._checkParams(self, params)
263 if self._getUser() is None:
264 return
265 if self._action == "":
266 #First call
267 afm = self._conf.getAbstractMgr().getAbstractFieldsMgr()
268 self._abstractData.title = self._abstract.getTitle()
269 for f in afm.getFields():
270 id = f.getId()
271 self._abstractData.setFieldValue(id, self._abstract.getField(id))
272 self._abstractData.type = self._abstract.getContribType()
273 trackIds = []
274 for track in self._abstract.getTrackListSorted():
275 trackIds.append(track.getId())
276 self._abstractData.tracks = trackIds
277 self._abstractData.comments = self._abstract.getComments()
279 def _processIfActive(self):
280 #We overload this method to allow modification after the CFA is closed if the modification deadline is after the submission deadline
281 cfaMgr = self._conf.getAbstractMgr()
282 modifDeadLine = cfaMgr.getModificationDeadline()
283 if not modifDeadLine:
284 modifDeadLine = cfaMgr.getEndSubmissionDate()
285 #if the user is in the autorized list, don't check period
286 if self._getUser() in cfaMgr.getAuthorizedSubmitterList():
287 return self._processIfOpened()
288 #if the submission period is not yet opened we show up a form informing
289 # about that.
290 if timezoneUtils.nowutc() < cfaMgr.getStartSubmissionDate():
291 #if the submission period is already closed we show up a form informing
292 # about that.
293 p = abstracts.WPCFANotYetOpened(self, self._conf)
294 return p.display()
295 #elif timezoneUtils.nowutc() > cfaMgr.getEndSubmissionDate() :
296 elif timezoneUtils.nowutc() > cfaMgr.getEndSubmissionDate() and timezoneUtils.nowutc() > modifDeadLine:
297 p = abstracts.WPCFAClosed(self, self._conf, True)
298 return p.display()
299 else:
300 return self._processIfOpened()
302 def _doValidate(self):
303 #First, one must validate that the information is fine
304 errors = self._abstractData.check()
305 if errors:
306 p = abstracts.WPAbstractModify(self, self._target)
307 pars = self._abstractData.toDict()
308 pars["action"] = self._action
309 # restart the current value of the param attachments to show the existing files
310 pars["attachments"] = self._abstract.getAttachments().values()
311 return p.display(**pars)
312 self._abstract.clearAuthors()
313 self._abstractData.setAbstractData(self._abstract)
314 self._redirect(urlHandlers.UHAbstractDisplay.getURL(self._abstract))
316 def _processIfOpened(self):
317 #check if the modification period is not over or if the abstract
318 # is in a different status than Submitted
319 if not self._conf.getAbstractMgr().inModificationPeriod() or \
320 not isinstance(self._abstract.getCurrentStatus(),
321 AbstractStatusSubmitted):
322 wp = abstracts.WPAbstractCannotBeModified(self, self._abstract)
323 return wp.display()
324 if self._action == "CANCEL":
325 self._redirect(urlHandlers.UHAbstractDisplay.getURL(self._abstract))
326 elif self._action == "VALIDATE":
327 return self._doValidate()
328 else:
329 p = abstracts.WPAbstractModify(self, self._target)
330 pars = self._abstractData.toDict()
331 pars["action"] = self._action
332 return p.display(**pars)
335 class RHUserAbstracts( RHAbstractSubmissionBase ):
336 _uh = urlHandlers.UHUserAbstracts
338 def _processIfActive( self ):
339 p = abstracts.WPUserAbstracts( self, self._conf )
340 return p.display()
343 class RHAbstractDisplayBase( RHAbstractSubmissionBase ):
345 def _checkParams( self, params ):
346 RHAbstractSubmissionBase._checkParams( self, params )
347 cfaMgr = self._conf.getAbstractMgr()
348 if not params.has_key("abstractId") and params.has_key("contribId"):
349 params["abstractId"] = params["contribId"]
350 self._abstract = self._target = cfaMgr.getAbstractById( params["abstractId"] )
351 if self._abstract == None:
352 raise NoReportError(_("The abstract you are trying to access does not exist or has been deleted"))
355 class RHAbstractSubmissionConfirmation( RHAbstractDisplayBase ):
356 _uh = urlHandlers.UHAbstractSubmissionConfirmation
358 def _processIfOpened( self ):
359 p = abstracts.WPAbstractSubmissionConfirmation( self, self._target )
360 return p.display()
363 class RHAbstractDisplay( RHAbstractDisplayBase ):
364 _uh = urlHandlers.UHAbstractDisplay
366 def _processIfActive( self ):
367 p = abstracts.WPAbstractDisplay( self, self._target )
368 return p.display()
371 class RHAbstractDisplayPDF( RHAbstractDisplayBase ):
373 def _checkProtection( self ):
374 RHConferenceBaseDisplay._checkProtection(self)
375 if not self._conf.getAbstractMgr().isActive() or not self._conf.hasEnabledSection("cfa"):
376 raise MaKaCError( _("The Call For Abstracts was disabled by the conference managers"))
378 def _process(self):
379 tz = timezoneUtils.DisplayTZ(self._aw, self._conf).getDisplayTZ()
380 filename = '%s - Abstract.pdf' % self._target.getTitle()
381 pdf = AbstractToPDF(self._target, tz=tz)
382 return send_file(filename, pdf.generate(), 'PDF')
385 class RHUserAbstractsPDF(RHAbstractSubmissionBase):
387 def _processIfActive(self):
388 tz = timezoneUtils.DisplayTZ(self._aw, self._conf).getDisplayTZ()
389 cfaMgr = self._conf.getAbstractMgr()
390 abstracts = set(cfaMgr.getAbstractListForAvatar(self._aw.getUser()))
391 abstracts |= set(cfaMgr.getAbstractListForAuthorEmail(self._aw.getUser().getEmail()))
392 self._abstractIds = sorted(abstract.getId() for abstract in abstracts)
393 if not self._abstractIds:
394 return _("No abstract to print")
396 filename = 'my-abstracts.pdf'
397 pdf = AbstractsToPDF(self._conf, self._abstractIds, tz=tz)
398 return send_file(filename, pdf.generate(), 'PDF')
401 class RHAbstractModificationBase(RHAbstractDisplayBase, RHModificationBaseProtected):
403 def _checkProtection(self):
404 RHModificationBaseProtected._checkProtection(self)
406 def _processIfActive(self):
407 #We overload this method to alow modification after the CFA is closed if the modification deadline is after the submission deadline
408 cfaMgr = self._conf.getAbstractMgr()
409 modifDeadLine = cfaMgr.getModificationDeadline()
410 if not modifDeadLine:
411 modifDeadLine = cfaMgr.getEndSubmissionDate()
412 #if the user is in the autorized list, don't check period
413 if self._getUser() in cfaMgr.getAuthorizedSubmitterList():
414 return self._processIfOpened()
415 #if the submission period is not yet opened we show up a form informing
416 # about that.
417 if timezoneUtils.nowutc() < cfaMgr.getStartSubmissionDate():
418 #if the submission period is already closed we show up a form informing
419 # about that.
420 p = abstracts.WPCFANotYetOpened(self, self._conf)
421 return p.display()
422 #elif timezoneUtils.nowutc() > cfaMgr.getEndSubmissionDate() :
423 elif timezoneUtils.nowutc() > cfaMgr.getEndSubmissionDate() and timezoneUtils.nowutc() > modifDeadLine:
424 p = abstracts.WPCFAClosed(self, self._conf, True)
425 return p.display()
426 else:
427 return self._processIfOpened()
430 class RHAbstractWithdraw( RHAbstractModificationBase ):
431 _uh = urlHandlers.UHAbstractWithdraw
433 def _checkParams( self, params ):
434 RHAbstractModificationBase._checkParams( self, params )
435 self._action = ""
436 self._comments = params.get( "comment", "" )
437 if params.has_key("OK"):
438 self._action = "WITHDRAW"
439 elif params.has_key("cancel"):
440 self._action = "CANCEL"
442 def _processIfOpened( self ):
443 if self._action == "CANCEL":
444 self._redirect( urlHandlers.UHAbstractDisplay.getURL( self._abstract ) )
445 elif self._action == "WITHDRAW":
446 if self._abstract.getCurrentStatus().__class__ not in \
447 [review.AbstractStatusSubmitted,
448 review.AbstractStatusUnderReview,
449 review.AbstractStatusInConflict,
450 review.AbstractStatusProposedToAccept,
451 review.AbstractStatusProposedToReject]:
452 raise MaKaCError( _("this abstract cannot be withdrawn, please contact the conference organisers in order to do so"))
453 self._abstract.withdraw(self._getUser(),self._comments)
454 self._redirect( urlHandlers.UHAbstractDisplay.getURL( self._abstract ) )
455 else:
456 wp = abstracts.WPAbstractWithdraw( self, self._abstract )
457 return wp.display()
460 class RHAbstractRecovery( RHAbstractModificationBase ):
461 _uh = urlHandlers.UHAbstractWithdraw
463 def _checkParams( self, params ):
464 RHAbstractModificationBase._checkParams( self, params )
465 self._action = ""
466 if params.has_key("OK"):
467 self._action = "RECOVER"
468 elif params.has_key("cancel"):
469 self._action = "CANCEL"
471 def _processIfOpened( self ):
472 if self._action == "CANCEL":
473 self._redirect( urlHandlers.UHAbstractDisplay.getURL( self._abstract ) )
474 elif self._action == "RECOVER":
475 status=self._abstract.getCurrentStatus()
476 if isinstance(status,review.AbstractStatusWithdrawn):
477 if status.getResponsible()!=self._getUser():
478 raise MaKaCError( _("you are not allowed to recover this abstract"))
479 self._abstract.recover()
480 self._redirect( urlHandlers.UHAbstractDisplay.getURL( self._abstract ) )
481 else:
482 wp = abstracts.WPAbstractRecovery( self, self._abstract )
483 return wp.display()
485 class RHGetAttachedFile(RHFileAccess):
487 def _checkProtection( self ):
488 if not self._conf.getAbstractMgr().showAttachedFilesContribList():
489 # Same protection as the abstract
490 temptarget=self._target
491 self._target = self._target.getOwner()
492 RHFileAccess._checkProtection( self )
493 self._target = temptarget