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 cStringIO
import StringIO
17 from flask
import request
19 import MaKaC
.webinterface
.urlHandlers
as urlHandlers
20 import MaKaC
.webinterface
.pages
.abstracts
as abstracts
21 import MaKaC
.review
as review
22 from MaKaC
.webinterface
.rh
.base
import RHModificationBaseProtected
23 from MaKaC
.webinterface
.rh
.conferenceBase
import RHAbstractBase
, RHConferenceBase
24 from MaKaC
.PDFinterface
.conference
import ConfManagerAbstractToPDF
25 from MaKaC
.common
.xmlGen
import XMLGen
26 from MaKaC
.errors
import MaKaCError
, ModificationError
, FormValuesError
, NoReportError
27 from MaKaC
.webinterface
.common
.abstractNotificator
import EmailNotificator
28 from indico
.core
.config
import Config
29 from MaKaC
.webinterface
.common
.abstractDataWrapper
import AbstractParam
30 from MaKaC
.i18n
import _
31 from MaKaC
.webinterface
.rh
.conferenceModif
import CFAEnabled
32 from MaKaC
.paperReviewing
import Answer
33 from MaKaC
.webinterface
.common
.tools
import cleanHTMLHeaderFilename
34 from indico
.web
.flask
.util
import send_file
35 from MaKaC
.PDFinterface
.base
import LatexRunner
38 class RHAbstractModifBase( RHAbstractBase
, RHModificationBaseProtected
):
39 """ Base class to be used for abstract modification in the admin interface,
40 when the request can only be performed by Conference managers.
43 def _checkParams( self
, params
):
44 RHAbstractBase
._checkParams
( self
, params
)
46 def _checkProtection( self
):
49 self
._target
= self
._conf
50 RHModificationBaseProtected
._checkProtection
(self
)
53 CFAEnabled
.checkEnabled(self
)
55 def _displayCustomPage( self
, wf
):
58 def _displayDefaultPage( self
):
62 wf
= self
.getWebFactory()
64 res
= self
._displayCustomPage
( wf
)
67 return self
._displayDefaultPage
()
70 class RHAbstractDelete(RHAbstractModifBase
):
72 def _checkParams( self
, params
):
73 RHAbstractModifBase
._checkParams
( self
, params
)
74 self
._remove
= params
.has_key("confirm")
75 self
._cancel
= params
.has_key("cancel")
79 self
._redirect
( urlHandlers
.UHAbstractModTools
.getURL( self
._abstract
) )
82 self
._conf
.getAbstractMgr().removeAbstract(self
._target
)
83 self
._redirect
( urlHandlers
.UHConfAbstractManagment
.getURL( self
._conf
) )
85 p
= abstracts
.WPModRemConfirmation( self
, self
._abstract
)
89 class RHAbstractManagment(RHAbstractModifBase
):
92 p
= abstracts
.WPAbstractManagment( self
, self
._target
)
93 return p
.display( **self
._getRequestParams
() )
96 class RHAbstractDirectAccess(RHAbstractModifBase
, RHConferenceBase
):
98 def _checkProtection( self
):
99 # if len( self._conf.getCoordinatedTracks( self._getUser() ) ) == 0:
100 RHAbstractModifBase
._checkProtection
( self
)
102 def _checkParams(self
, params
):
103 RHConferenceBase
._checkParams
(self
, params
)
104 self
._abstractId
= params
.get("abstractId","")
105 self
._abstractExist
= False
107 abstract
= self
._conf
.getAbstractMgr().getAbstractById(self
._abstractId
)
108 self
._abstractExist
= True
109 RHAbstractModifBase
._checkParams
(self
, params
)
114 def _process( self
):
115 if self
._abstractExist
and self
._target
is not None:
116 p
= abstracts
.WPAbstractManagment( self
, self
._target
)
117 return p
.display( **self
._getRequestParams
() )
119 url
= urlHandlers
.UHConfAbstractManagment
.getURL(self
._conf
)
120 #url.addParam("directAbstractMsg","There is no abstract number %s in this conference"%self._abstractId)
125 class RHAbstractToPDF(RHAbstractModifBase
):
127 def _process( self
):
128 tz
= self
._conf
.getTimezone()
129 filename
= "%s - Abstract.pdf" % self
._target
.getTitle()
130 pdf
= ConfManagerAbstractToPDF(self
._target
, tz
=tz
)
131 return send_file(filename
, pdf
.generate(), 'PDF')
134 class RHAbstractToXML(RHAbstractModifBase
):
136 def _process( self
):
137 filename
= "%s - Abstract.xml"%self
._target
.getTitle()
140 x
.openTag("abstract")
141 x
.writeTag("Id", self
._target
.getId())
142 x
.writeTag("Title", self
._target
.getTitle())
143 afm
= self
._target
.getConference().getAbstractMgr().getAbstractFieldsMgr()
144 for f
in afm
.getFields():
146 if f
.isActive() and str(self
._target
.getField(id)).strip() != "":
147 x
.writeTag("field",self
._target
.getField(id),[("id",id)])
148 x
.writeTag("Conference", self
._target
.getConference().getTitle())
150 for au
in self
._target
.getAuthorList():
151 if self
._target
.isPrimaryAuthor(au
):
152 x
.openTag("PrimaryAuthor")
153 x
.writeTag("FirstName", au
.getFirstName())
154 x
.writeTag("FamilyName", au
.getSurName())
155 x
.writeTag("Email", au
.getEmail())
156 x
.writeTag("Affiliation", au
.getAffiliation())
157 x
.closeTag("PrimaryAuthor")
162 x
.openTag("Co-Author")
163 x
.writeTag("FirstName", au
.getFirstName())
164 x
.writeTag("FamilyName", au
.getSurName())
165 x
.writeTag("Email", au
.getEmail())
166 x
.writeTag("Affiliation", au
.getAffiliation())
167 x
.closeTag("Co-Author")
169 for au
in self
._target
.getSpeakerList():
171 x
.writeTag("FirstName", au
.getFirstName ())
172 x
.writeTag("FamilyName", au
.getSurName())
173 x
.writeTag("Email", au
.getEmail())
174 x
.writeTag("Affiliation", au
.getAffiliation())
175 x
.closeTag("Speaker")
177 #To change for the new contribution type system to:
178 #x.writeTag("ContributionType", self._target.getContribType().getName())
179 x
.writeTag("ContributionType", self
._target
.getContribType())
181 for t
in self
._target
.getTrackList():
182 x
.writeTag("Track", t
.getTitle())
184 x
.closeTag("abstract")
186 return send_file(filename
, StringIO(x
.getXml()), 'XML')
189 class _AbstractWrapper
:
191 def __init__(self
,status
):
194 def getCurrentStatus(self
):
198 class RHAbstractManagmentAccept(RHAbstractModifBase
):
200 def _checkParams( self
, params
):
201 RHAbstractModifBase
._checkParams
( self
, params
)
202 self
._accept
= params
.get("accept", None)
203 self
._warningShown
=params
.has_key("confirm")
204 self
._trackId
= params
.get("track", "")
205 self
._track
=self
._conf
.getTrackById(params
.get("track", ""))
206 self
._sessionId
= params
.get("session", "")
207 self
._session
=self
._conf
.getSessionById(params
.get("session", ""))
208 self
._comments
= params
.get("comments", "")
209 self
._typeId
= params
.get("type", "")
210 self
._doNotify
=params
.has_key("notify")
212 def _process( self
):
214 cType
=self
._conf
.getContribTypeById(self
._typeId
)
215 st
=review
.AbstractStatusAccepted(self
._target
,None,self
._track
,cType
)
216 wrapper
=_AbstractWrapper(st
)
217 tpl
=self
._target
.getOwner().getNotifTplForAbstract(wrapper
)
218 if self
._doNotify
and not self
._warningShown
and tpl
is None:
219 p
=abstracts
.WPModAcceptConfirmation(self
,self
._target
)
220 return p
.display(track
=self
._trackId
,comments
=self
._comments
,type=self
._typeId
,session
=self
._sessionId
)
222 self
._target
.accept(self
._getUser
(),self
._track
,cType
,self
._comments
,self
._session
)
225 self
._target
.notify(n
,self
._getUser
())
226 self
._redirect
(urlHandlers
.UHAbstractManagment
.getURL(self
._abstract
))
228 p
= abstracts
.WPAbstractManagmentAccept( self
, self
._target
)
229 return p
.display( **self
._getRequestParams
() )
232 class RHAbstractManagmentReject(RHAbstractModifBase
):
234 def _checkParams( self
, params
):
235 RHAbstractModifBase
._checkParams
( self
, params
)
236 self
._reject
= params
.get("reject", None)
237 self
._comments
= params
.get("comments", "")
238 self
._doNotify
=params
.has_key("notify")
239 self
._warningShown
=params
.has_key("confirm")
241 def _process( self
):
243 st
=review
.AbstractStatusRejected(self
._target
,None,None)
244 wrapper
=_AbstractWrapper(st
)
245 tpl
=self
._target
.getOwner().getNotifTplForAbstract(wrapper
)
246 if self
._doNotify
and not self
._warningShown
and tpl
is None:
247 p
=abstracts
.WPModRejectConfirmation(self
,self
._target
)
248 return p
.display(comments
=self
._comments
)
250 self
._target
.reject(self
._getUser
(), self
._comments
)
253 self
._target
.notify(n
,self
._getUser
())
254 self
._redirect
(urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
256 p
= abstracts
.WPAbstractManagmentReject( self
, self
._target
)
257 return p
.display( **self
._getRequestParams
() )
260 class RHMarkAsDup(RHAbstractModifBase
):
262 def _checkParams(self
, params
):
263 RHAbstractModifBase
._checkParams
(self
, params
)
264 self
._action
, self
._comments
, self
._original
= "", "", None
265 self
._originalId
= ""
267 self
._action
= "MARK"
268 self
._comments
= params
.get("comments", "")
269 self
._originalId
= params
.get("id", "")
270 self
._original
= self
._target
.getOwner().getAbstractById(self
._originalId
)
272 def _getErrorsInData(self
):
274 if self
._original
is None or self
._target
== self
._original
:
275 res
.append(_("invalid original abstract id"))
280 if self
._action
== "MARK":
281 errorList
= self
._getErrorsInData
()
282 if len(errorList
) == 0:
283 self
._target
.markAsDuplicated(
284 self
._getUser
(), self
._original
, self
._comments
)
286 urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
289 errMsg
= "<br>".join(errorList
)
290 p
= abstracts
.WPModMarkAsDup(self
, self
._target
)
291 return p
.display(comments
=self
._comments
, originalId
=self
._originalId
, errorMsg
=errMsg
)
294 class RHUnMarkAsDup(RHAbstractModifBase
):
296 def _checkParams(self
, params
):
297 RHAbstractModifBase
._checkParams
(self
, params
)
298 self
._action
, self
._comments
, self
._original
= "", "", None
299 self
._originalId
= ""
301 self
._action
= "UNMARK"
302 self
._comments
= params
.get("comments", "")
306 if self
._action
== "UNMARK":
307 self
._target
.unMarkAsDuplicated(self
._getUser
(), self
._comments
)
309 urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
311 p
= abstracts
.WPModUnMarkAsDup(self
, self
._target
)
312 return p
.display(comments
=self
._comments
, originalId
=self
._originalId
, errorMsg
=errMsg
)
315 class RHMergeInto(RHAbstractModifBase
):
317 def _checkParams(self
, params
):
318 RHAbstractModifBase
._checkParams
(self
, params
)
319 self
._action
, self
._comments
, self
._targetAbs
= "", "", None
320 self
._targetAbsId
, self
._includeAuthors
, self
._doNotify
= "", False, True
322 self
._action
= "MERGE"
323 self
._comments
= params
.get("comments", "")
324 self
._targetAbsId
= params
.get("id", "")
325 self
._includeAuthors
= "includeAuthors" in params
326 self
._doNotify
= "notify" in params
327 self
._targetAbs
= self
._target
.getOwner(
328 ).getAbstractById(self
._targetAbsId
)
330 def _getErrorsInData(self
):
332 if self
._targetAbs
is None or self
._target
== self
._targetAbs
:
333 res
.append("invalid target abstract id")
338 if self
._action
== "MERGE":
339 errorList
= self
._getErrorsInData
()
340 if len(errorList
) == 0:
341 self
._target
.mergeInto(self
._getUser
(), self
._targetAbs
, comments
=self
._comments
, mergeAuthors
=self
._includeAuthors
)
343 self
._target
.notify(EmailNotificator(), self
._getUser
())
345 urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
348 errMsg
= "<br>".join(errorList
)
349 p
= abstracts
.WPModMergeInto(self
, self
._target
)
350 return p
.display(comments
=self
._comments
, targetId
=self
._targetAbsId
, errorMsg
=errMsg
, includeAuthors
=self
._includeAuthors
, notify
=self
._doNotify
)
352 class RHUnMerge(RHAbstractModifBase
):
354 def _checkParams( self
, params
):
355 RHAbstractModifBase
._checkParams
( self
, params
)
356 self
._action
,self
._comments
="",""
357 if params
.has_key("OK"):
358 self
._action
="UNMERGE"
359 self
._comments
=params
.get("comments","")
362 def _process( self
):
363 if self
._action
=="UNMERGE":
364 self
._target
.unMerge(self
._getUser
(),self
._comments
)
365 self
._redirect
(urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
367 p
=abstracts
.WPModUnMerge(self
,self
._target
)
368 return p
.display(comments
=self
._comments
)
371 class RHPropBase(RHAbstractModifBase
):
373 def _checkProtection(self
):
375 RHAbstractModifBase
._checkProtection
(self
)
376 except ModificationError
,e
:
377 if self
._target
.isAllowedToCoordinate(self
._getUser
()):
381 def _checkParams( self
, params
):
382 RHAbstractModifBase
._checkParams
(self
, params
)
385 if params
.has_key("OK"):
387 conf
=self
._target
.getConference()
388 self
._track
= conf
.getTrackById(params
.get("track",""))
389 if self
._track
is None:
390 raise FormValuesError( _("You have to choose a track in order to do the proposal. If there are not tracks to select, please change the track assignment of the abstract from its management page"))
391 self
._contribType
= self
._conf
.getContribTypeById(params
.get("contribType",""))
392 self
._comment
= params
.get("comment","")
393 scaleLower
= conf
.getConfAbstractReview().getScaleLower()
394 scaleHigher
= conf
.getConfAbstractReview().getScaleHigher()
395 numberOfAnswers
= conf
.getConfAbstractReview().getNumberOfAnswers()
397 for question
in conf
.getConfAbstractReview().getReviewingQuestions():
399 if not params
.has_key("RB_"+str(c
)):
400 raise FormValuesError(_("Please, reply to all the reviewing questions. Question \"%s\" is missing the answer.")%question
.getText())
401 rbValue
= int(params
.get("RB_"+str(c
),scaleLower
))
402 newId
= conf
.getConfAbstractReview().getNewAnswerId()
403 newAnswer
= Answer(newId
, rbValue
, numberOfAnswers
, question
)
404 newAnswer
.calculateRatingValue(scaleLower
, scaleHigher
)
405 self
._answers
.append(newAnswer
)
406 elif params
.has_key("CANCEL"):
407 self
._action
="CANCEL"
410 class RHPropToAcc(RHPropBase
):
412 def _process( self
):
413 url
=urlHandlers
.UHAbstractManagment
.getURL(self
._target
)
414 if self
._action
=="GO":
415 self
._abstract
.proposeToAccept(self
._getUser
(),\
416 self
._track
,self
._contribType
,self
._comment
, self
._answers
)
418 elif self
._action
=="CANCEL":
421 p
=abstracts
.WPModPropToAcc(self
,self
._target
)
426 class RHPropToRej(RHPropBase
):
428 def _process( self
):
429 url
=urlHandlers
.UHAbstractManagment
.getURL(self
._target
)
430 if self
._action
=="GO":
431 self
._abstract
.proposeToReject(self
._getUser
(),\
432 self
._track
,self
._comment
, self
._answers
)
434 elif self
._action
=="CANCEL":
437 p
=abstracts
.WPModPropToRej(self
,self
._target
)
441 class RHWithdraw(RHAbstractModifBase
):
443 def _checkParams(self
,params
):
444 RHAbstractModifBase
._checkParams
(self
,params
)
445 self
._action
,self
._comments
="",""
446 if params
.has_key("OK"):
447 self
._action
="WITHDRAW"
448 self
._comment
=params
.get("comment","")
449 if params
.has_key("CANCEL"):
450 self
._action
="CANCEL"
452 def _process( self
):
453 url
=urlHandlers
.UHAbstractManagment
.getURL(self
._target
)
454 if self
._action
=="WITHDRAW":
455 self
._target
.withdraw(self
._getUser
(),self
._comment
)
458 elif self
._action
=="CANCEL":
462 p
=abstracts
.WPModWithdraw(self
,self
._target
)
465 class RHBackToSubmitted(RHAbstractModifBase
):
467 def _removeAssociatedContribution(self
):
468 contribution
= self
._abstract
.getContribution()
469 contribution
.getOwner().getSchedule().removeEntry(contribution
.getSchEntry())
470 contribution
.delete()
472 def _process( self
):
473 url
=urlHandlers
.UHAbstractManagment
.getURL(self
._target
)
474 if isinstance(self
._abstract
.getCurrentStatus(), (review
.AbstractStatusWithdrawn
, review
.AbstractStatusRejected
)):
475 self
._abstract
.setCurrentStatus(review
.AbstractStatusSubmitted(self
._abstract
))
476 elif isinstance(self
._abstract
.getCurrentStatus(), review
.AbstractStatusAccepted
):
477 # remove the associated contribution
478 self
._removeAssociatedContribution
()
479 # set submittted status
480 self
._abstract
.setCurrentStatus(review
.AbstractStatusSubmitted(self
._abstract
))
484 class RHAbstractManagmentChangeTrack(RHAbstractModifBase
):
486 def _checkParams( self
, params
):
487 self
._cancel
= params
.get("cancel", None)
488 self
._save
= params
.get("save", None)
489 self
._tracks
= self
._normaliseListParam
(params
.get("tracks", []))
490 RHAbstractModifBase
._checkParams
( self
, params
)
492 def _process( self
):
495 for trackId
in self
._tracks
:
496 tracks
.append( self
._conf
.getTrackById(trackId
) )
497 self
._target
.setTracks( tracks
)
498 self
._redirect
(urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
500 self
._redirect
(urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
502 p
= abstracts
.WPAbstractManagmentChangeTrack( self
, self
._target
)
503 return p
.display( **self
._getRequestParams
() )
506 class RHAbstractTrackManagment(RHAbstractModifBase
):
508 def _process( self
):
509 p
= abstracts
.WPAbstractTrackManagment( self
, self
._target
)
510 return p
.display( **self
._getRequestParams
() )
512 class RHAbstractTrackOrderByRating(RHAbstractModifBase
):
514 def _process( self
):
515 p
= abstracts
.WPAbstractTrackOrderByRating( self
, self
._target
)
516 return p
.display( **self
._getRequestParams
() )
519 class RHEditData(RHAbstractModifBase
, AbstractParam
):
522 RHAbstractModifBase
.__init
__(self
)
523 AbstractParam
.__init
__(self
)
525 def _checkParams(self
, params
):
526 RHAbstractModifBase
._checkParams
(self
,params
)
527 if self
._getUser
() is None:
529 AbstractParam
._checkParams
(self
, params
, self
._conf
, request
.content_length
)
530 if self
._action
== "":#First call
531 #TODO: remove this code, this should be handle by AbstractData (not method available
532 # because setAbstractData(abstract) is used when saving, so there are specific actions.
533 afm
= self
._conf
.getAbstractMgr().getAbstractFieldsMgr()
534 self
._abstractData
.title
= self
._abstract
.getTitle()
535 for f
in afm
.getFields():
537 self
._abstractData
.setFieldValue(id, self
._abstract
.getField(id))
538 self
._abstractData
.type = self
._abstract
.getContribType()
540 for track
in self
._abstract
.getTrackListSorted():
541 trackIds
.append(track
.getId())
542 self
._abstractData
.tracks
= trackIds
543 self
._abstractData
.comments
= self
._abstract
.getComments()
545 def _doValidate( self
):
546 #First, one must validate that the information is fine
547 errors
= self
._abstractData
.check()
549 p
= abstracts
.WPModEditData(self
, self
._target
, self
._abstractData
)
550 pars
= self
._abstractData
.toDict()
551 pars
["errors"] = errors
552 pars
["action"] = self
._action
553 # restart the current value of the param attachments to show the existing files
554 pars
["attachments"] = self
._abstract
.getAttachments().values()
555 return p
.display( **pars
)
556 self
._abstract
.clearAuthors()
557 #self._setAbstractData(self._abstract)
558 self
._abstractData
.setAbstractData(self
._abstract
)
559 self
._redirect
(urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
561 def _process( self
):
562 if self
._action
== "CANCEL":
563 self
._redirect
(urlHandlers
.UHAbstractManagment
.getURL(self
._target
))
564 elif self
._action
== "VALIDATE":
565 return self
._doValidate
()
567 if isinstance( self
._abstract
.getCurrentStatus(), review
.AbstractStatusAccepted
):
568 raise NoReportError(_("The abstract with id '%s' cannot be edited because it has already been accepted.") % self
._abstract
.getId())
569 p
= abstracts
.WPModEditData(self
, self
._target
, self
._abstractData
)
570 pars
= self
._abstractData
.toDict()
571 return p
.display(**pars
)
574 class RHIntComments(RHAbstractModifBase
):
576 def _process( self
):
577 p
= abstracts
.WPModIntComments(self
,self
._target
)
581 class RHNewIntComment(RHIntComments
):
583 def _checkParams(self
,params
):
584 RHIntComments
._checkParams
(self
,params
)
586 if params
.has_key("OK"):
587 self
._action
="UPDATE"
588 self
._content
=params
.get("content","")
589 elif params
.has_key("CANCEL"):
590 self
._action
="CANCEL"
592 def _process( self
):
593 if self
._action
=="UPDATE":
594 c
=review
.Comment(self
._getUser
())
595 c
.setContent(self
._content
)
596 self
._target
.addIntComment(c
)
597 self
._redirect
(urlHandlers
.UHAbstractModIntComments
.getURL(self
._target
))
599 elif self
._action
=="CANCEL":
600 self
._redirect
(urlHandlers
.UHAbstractModIntComments
.getURL(self
._target
))
602 p
= abstracts
.WPModNewIntComment(self
,self
._target
)
606 class RHIntCommentBase(RHAbstractModifBase
):
608 def _checkParams(self
,params
):
609 RHAbstractModifBase
._checkParams
(self
,params
)
610 id=params
.get("intCommentId","")
612 raise MaKaCError( _("the internal comment identifier hasn't been specified"))
613 abstract
=self
._target
614 self
._target
=abstract
.getIntCommentById(id)
617 class RHIntCommentRem(RHIntCommentBase
):
620 abstract
=self
._target
.getAbstract()
621 abstract
.removeIntComment(self
._target
)
622 self
._redirect
(urlHandlers
.UHAbstractModIntComments
.getURL(abstract
))
625 class RHIntCommentEdit(RHIntCommentBase
):
627 def _checkParams(self
,params
):
628 RHIntCommentBase
._checkParams
(self
,params
)
630 if params
.has_key("OK"):
631 self
._action
="UPDATE"
632 self
._content
=params
.get("content","")
633 elif params
.has_key("CANCEL"):
634 self
._action
="CANCEL"
637 if self
._action
=="UPDATE":
638 self
._target
.setContent(self
._content
)
639 self
._redirect
(urlHandlers
.UHAbstractModIntComments
.getURL(self
._target
.getAbstract()))
641 elif self
._action
=="CANCEL":
642 self
._redirect
(urlHandlers
.UHAbstractModIntComments
.getURL(self
._target
.getAbstract()))
644 p
=abstracts
.WPModIntCommentEdit(self
,self
._target
)
648 class RHNotifLog(RHAbstractModifBase
):
650 def _process( self
):
651 p
= abstracts
.WPModNotifLog(self
,self
._target
)
654 class RHTools(RHAbstractModifBase
):
656 def _process( self
):
657 p
= abstracts
.WPModTools(self
,self
._target
)