| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | ## |
|---|
| 3 | ## |
|---|
| 4 | ## This file is part of CDS Indico. |
|---|
| 5 | ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN. |
|---|
| 6 | ## |
|---|
| 7 | ## CDS Indico is free software; you can redistribute it and/or |
|---|
| 8 | ## modify it under the terms of the GNU General Public License as |
|---|
| 9 | ## published by the Free Software Foundation; either version 2 of the |
|---|
| 10 | ## License, or (at your option) any later version. |
|---|
| 11 | ## |
|---|
| 12 | ## CDS Indico is distributed in the hope that it will be useful, but |
|---|
| 13 | ## WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 15 | ## General Public License for more details. |
|---|
| 16 | ## |
|---|
| 17 | ## You should have received a copy of the GNU General Public License |
|---|
| 18 | ## along with CDS Indico; if not, write to the Free Software Foundation, Inc., |
|---|
| 19 | ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 20 | |
|---|
| 21 | from MaKaC.plugins.InstantMessaging.notificationComponents import IInstantMessagingListener |
|---|
| 22 | from MaKaC.plugins.base import Observable, PluginsHolder |
|---|
| 23 | from MaKaC.plugins.util import PluginsWrapper, PluginFieldsWrapper |
|---|
| 24 | from MaKaC.plugins.InstantMessaging.XMPP.helpers import GeneralLinkGenerator |
|---|
| 25 | from MaKaC.plugins.helpers import DBHelpers, MailHelper |
|---|
| 26 | from MaKaC.plugins.InstantMessaging.indexes import IndexByConf, IndexByCRName, IndexByID, IndexByUser |
|---|
| 27 | from MaKaC.plugins.InstantMessaging import urlHandlers |
|---|
| 28 | from MaKaC.i18n import _ |
|---|
| 29 | from MaKaC.conference import ConferenceHolder |
|---|
| 30 | from MaKaC.common.contextManager import ContextManager |
|---|
| 31 | from MaKaC.common.externalOperationsManager import ExternalOperationsManager |
|---|
| 32 | from MaKaC.common.mail import GenericMailer |
|---|
| 33 | from MaKaC.common.info import HelperMaKaCInfo |
|---|
| 34 | from MaKaC.webinterface import wcomponents |
|---|
| 35 | from MaKaC.webinterface.mail import GenericNotification |
|---|
| 36 | from MaKaC.services.interface.rpc.common import ServiceError, NoReportError |
|---|
| 37 | import zope.interface |
|---|
| 38 | |
|---|
| 39 | from indico.core.extpoint import Component |
|---|
| 40 | from indico.core.extpoint.conference import INavigationContributor |
|---|
| 41 | |
|---|
| 42 | |
|---|
| 43 | class ChatSMContributor(Component, Observable): |
|---|
| 44 | |
|---|
| 45 | zope.interface.implements(INavigationContributor) |
|---|
| 46 | |
|---|
| 47 | """ChatSideMenuContributor. Fills the side menu of the conference's management menu when |
|---|
| 48 | the chat plugin is active""" |
|---|
| 49 | |
|---|
| 50 | @classmethod |
|---|
| 51 | def fillManagementSideMenu(cls, obj, params={}): |
|---|
| 52 | params['Instant Messaging'] = wcomponents.SideMenuItem(_("Chat Rooms"), |
|---|
| 53 | urlHandlers.UHConfModifChat.getURL( obj._conf )) |
|---|
| 54 | |
|---|
| 55 | @classmethod |
|---|
| 56 | def getActiveNavigationItem(cls, obj, params={}): |
|---|
| 57 | pass |
|---|
| 58 | |
|---|
| 59 | @classmethod |
|---|
| 60 | def confDisplaySMFillDict(cls, obj, params): |
|---|
| 61 | sideMenuItemsDict = params['dict'] |
|---|
| 62 | conf = params['conf'] |
|---|
| 63 | |
|---|
| 64 | sideMenuItemsDict["instantMessaging"] = { \ |
|---|
| 65 | "caption": _("Chat Rooms"), \ |
|---|
| 66 | "URL": str(urlHandlers.UHConferenceInstantMessaging.getURL(conf)), \ |
|---|
| 67 | "staticURL": "", \ |
|---|
| 68 | "parent": ""} |
|---|
| 69 | |
|---|
| 70 | @classmethod |
|---|
| 71 | def confDisplaySMFillOrderedKeys(cls, obj, list): |
|---|
| 72 | list.append("instantMessaging") |
|---|
| 73 | |
|---|
| 74 | |
|---|
| 75 | @classmethod |
|---|
| 76 | def confDisplaySMShow(cls, obj, params): |
|---|
| 77 | obj._instantMessaging = obj._sectionMenu.getLinkByName("instantMessaging") |
|---|
| 78 | if not DBHelpers.roomsToShow(obj._conf): |
|---|
| 79 | obj._instantMessaging.setVisible(False) |
|---|
| 80 | |
|---|
| 81 | |
|---|
| 82 | @classmethod |
|---|
| 83 | def meetingAndLectureDisplay(cls, obj, params): |
|---|
| 84 | out = params['out'] |
|---|
| 85 | conf = params['conf'] |
|---|
| 86 | if DBHelpers.roomsToShow(conf): |
|---|
| 87 | linksList = PluginsHolder().getPluginType('InstantMessaging').getOption('customLinks').getValue() |
|---|
| 88 | out.openTag("chatrooms") |
|---|
| 89 | for chatroom in DBHelpers.getShowableRooms(conf): |
|---|
| 90 | out.openTag("chatroom") |
|---|
| 91 | |
|---|
| 92 | out.writeTag("id", chatroom.getId()) |
|---|
| 93 | out.writeTag("name", chatroom.getTitle()) |
|---|
| 94 | out.writeTag("server", 'conference.' + chatroom.getHost() if chatroom.getCreatedInLocalServer() else chatroom.getHost()) |
|---|
| 95 | out.writeTag("description", chatroom.getDescription()) |
|---|
| 96 | out.writeTag("reqPassword", _('Yes') if chatroom.getPassword() else _('No')) |
|---|
| 97 | out.writeTag("showPassword", chatroom.getShowPass()) |
|---|
| 98 | out.writeTag("password", chatroom.getPassword()) |
|---|
| 99 | out.writeTag("createdInLocalServer", chatroom.getCreatedInLocalServer()) |
|---|
| 100 | out.openTag("links") |
|---|
| 101 | if linksList.__len__() > 0: |
|---|
| 102 | out.writeTag("linksToShow", 'true') |
|---|
| 103 | else: |
|---|
| 104 | out.writeTag("linksToShow", 'false') |
|---|
| 105 | |
|---|
| 106 | for link in linksList: |
|---|
| 107 | out.openTag("customLink") |
|---|
| 108 | out.writeTag("name", link['name']) |
|---|
| 109 | out.writeTag("structure", GeneralLinkGenerator(chatroom, link['structure']).generate()) |
|---|
| 110 | out.closeTag("customLink") |
|---|
| 111 | |
|---|
| 112 | out.closeTag("links") |
|---|
| 113 | out.closeTag("chatroom") |
|---|
| 114 | out.closeTag("chatrooms") |
|---|
| 115 | |
|---|
| 116 | out.writeTag("how2connect", PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('ckEditor')) |
|---|
| 117 | |
|---|
| 118 | @classmethod |
|---|
| 119 | def addCheckBox2CloneConf(cls, obj, list): |
|---|
| 120 | """ we show the clone checkbox if: |
|---|
| 121 | * The XMPP Plugin is active. |
|---|
| 122 | * There are rooms in the event created by the user who wants to clone |
|---|
| 123 | """ |
|---|
| 124 | #list of creators of the chat rooms |
|---|
| 125 | ownersList = [cr.getOwner() for cr in DBHelpers().getChatroomList(obj._conf)] |
|---|
| 126 | if PluginsWrapper('InstantMessaging', 'XMPP').isActive() and obj._rh._aw._currentUser in ownersList: |
|---|
| 127 | list['cloneOptions'] += _("""<li><input type="checkbox" name="cloneChatrooms" id="cloneChatrooms" value="1" />_("Chat Rooms")</li>""") |
|---|
| 128 | |
|---|
| 129 | @classmethod |
|---|
| 130 | def fillCloneDict(self, obj, params): |
|---|
| 131 | options = params['options'] |
|---|
| 132 | paramNames = params['paramNames'] |
|---|
| 133 | options['chatrooms'] = 'cloneChatrooms' in paramNames |
|---|
| 134 | |
|---|
| 135 | @classmethod |
|---|
| 136 | def cloneEvent(cls, confToClone, params): |
|---|
| 137 | """ we'll clone only the chat rooms created by the user who is cloning the conference """ |
|---|
| 138 | conf = params['conf'] |
|---|
| 139 | user = params['user'] |
|---|
| 140 | options = params['options'] |
|---|
| 141 | ContextManager.getdefault('mailHelper', MailHelper()) |
|---|
| 142 | |
|---|
| 143 | if options.get("chatrooms", True): |
|---|
| 144 | crList = DBHelpers().getChatroomList(confToClone) |
|---|
| 145 | ownersList = [cr.getOwner() for cr in crList] |
|---|
| 146 | if PluginsWrapper('InstantMessaging', 'XMPP').isActive(): |
|---|
| 147 | for cr in crList: |
|---|
| 148 | if user is cr.getOwner(): |
|---|
| 149 | cls()._notify('addConference2Room', {'room':cr, 'conf':conf.getId()}) |
|---|
| 150 | |
|---|
| 151 | ContextManager.get('mailHelper').sendMails() |
|---|
| 152 | |
|---|
| 153 | |
|---|
| 154 | class ChatroomStorage(Component): |
|---|
| 155 | zope.interface.implements(IInstantMessagingListener) |
|---|
| 156 | |
|---|
| 157 | def __init__(self): |
|---|
| 158 | #it's the first thing to do when having these events |
|---|
| 159 | self.priority=1 |
|---|
| 160 | |
|---|
| 161 | @classmethod |
|---|
| 162 | def createChatroom(cls, obj, params): |
|---|
| 163 | """ Inserts the object in the database according to all the kind of indexing types, in this case: |
|---|
| 164 | -Chat rooms by conference |
|---|
| 165 | -Chat rooms by user |
|---|
| 166 | -Chat rooms by name (to check if there's already a chat room with that name in our XMPP server) |
|---|
| 167 | -Chat rooms by ID (to access faster to the object when querying) |
|---|
| 168 | """ |
|---|
| 169 | room = params['room'] |
|---|
| 170 | |
|---|
| 171 | conference = params['conference'] |
|---|
| 172 | |
|---|
| 173 | # index by conference id |
|---|
| 174 | confIndex = IndexByConf() |
|---|
| 175 | room.setId(DBHelpers.newID()) |
|---|
| 176 | confIndex.index(conference.getId(), room) |
|---|
| 177 | |
|---|
| 178 | # Index by chat room's name |
|---|
| 179 | crNameIndex = IndexByCRName() |
|---|
| 180 | crNameIndex.index(room) |
|---|
| 181 | |
|---|
| 182 | # Index by id |
|---|
| 183 | idIndex = IndexByID() |
|---|
| 184 | idIndex.index(room) |
|---|
| 185 | |
|---|
| 186 | # Index by room creator |
|---|
| 187 | userIndex = IndexByUser() |
|---|
| 188 | userIndex.index(room.getOwner().getId(), room) |
|---|
| 189 | |
|---|
| 190 | @classmethod |
|---|
| 191 | def editChatroom(self, obj, params): |
|---|
| 192 | oldTitle = params['oldTitle'] |
|---|
| 193 | newRoom = params['newRoom'] |
|---|
| 194 | #we have an index by the chat room name. If, while editing, someone changes the chat room name, we'll have to update the index |
|---|
| 195 | if oldTitle != newRoom.getTitle(): |
|---|
| 196 | #the title has been changed. Get rid of the old index and substitute it for the new one |
|---|
| 197 | crNameIndex = IndexByCRName() |
|---|
| 198 | crNameIndex.unindex(newRoom, oldTitle) |
|---|
| 199 | crNameIndex.index(newRoom) |
|---|
| 200 | |
|---|
| 201 | @classmethod |
|---|
| 202 | def deleteChatroom(cls, obj, params): |
|---|
| 203 | """ Deletes the chat room in the database according to all kind of indexing types""" |
|---|
| 204 | |
|---|
| 205 | chatroom = params['room'] |
|---|
| 206 | |
|---|
| 207 | confId = obj._conferenceID |
|---|
| 208 | #if we have the same room used in two or more different conferences, we just delete it from the |
|---|
| 209 | #conferences list in the chat room and from the IndexByConf index |
|---|
| 210 | chatroom.getConferences().pop(confId) |
|---|
| 211 | confIndex = IndexByConf() |
|---|
| 212 | confIndex.unindex(confId, chatroom) |
|---|
| 213 | |
|---|
| 214 | if len(chatroom.getConferences()) is 0: |
|---|
| 215 | #there are no more references to the chat room, we completely delete it |
|---|
| 216 | crNameIndex = IndexByCRName() |
|---|
| 217 | crNameIndex.unindex(chatroom) |
|---|
| 218 | |
|---|
| 219 | idIndex = IndexByID() |
|---|
| 220 | idIndex.unindex(chatroom) |
|---|
| 221 | |
|---|
| 222 | userIndex = IndexByUser() |
|---|
| 223 | userIndex.unindex(chatroom.getOwner().getId(), chatroom) |
|---|
| 224 | |
|---|
| 225 | @classmethod |
|---|
| 226 | def addConference2Room(cls, obj, params): |
|---|
| 227 | """ When we re use a chat room for another conference(s), this is the method called. |
|---|
| 228 | It may be called from the AJAX service, but also from the clone event. Since we |
|---|
| 229 | don't know it, we have to check the parameters accordingly""" |
|---|
| 230 | room = params['room'] |
|---|
| 231 | confId = params['conf'] |
|---|
| 232 | |
|---|
| 233 | confIndex = IndexByConf() |
|---|
| 234 | confIndex.index(confId, room) |
|---|
| 235 | |
|---|
| 236 | |
|---|
| 237 | class ChatroomMailer(Component): |
|---|
| 238 | """ Sends emails to the administrators when a chat room related event happens""" |
|---|
| 239 | |
|---|
| 240 | zope.interface.implements(IInstantMessagingListener) |
|---|
| 241 | |
|---|
| 242 | @classmethod |
|---|
| 243 | def createChatroom(cls, obj, params): |
|---|
| 244 | room = params['room'] |
|---|
| 245 | # we give this operation the name createSomething in the externaloperationsmanager |
|---|
| 246 | # we will execute the method performOperation in a chat room creation, which will send an email to all the requested users |
|---|
| 247 | # saying that a chat room was created. We pass the 2 mandatory arguments and finally the arguments required for performOperation |
|---|
| 248 | # (in this case, only one argument) |
|---|
| 249 | ExternalOperationsManager.execute(cls, "create_"+str(cls.__class__)+str(room.getId()), cls.performOperation, 'create', room.getConferences().values()[0], room, room) |
|---|
| 250 | |
|---|
| 251 | @classmethod |
|---|
| 252 | def editChatroom(cls, obj, params): |
|---|
| 253 | room = params['newRoom'] |
|---|
| 254 | conf = ConferenceHolder().getById(obj._conferenceID) |
|---|
| 255 | ExternalOperationsManager.execute(cls, "edit_"+str(cls.__class__)+str(room.getId()), cls.performOperation, 'edit', conf, room, room, conf) |
|---|
| 256 | |
|---|
| 257 | @classmethod |
|---|
| 258 | def deleteChatroom(cls, obj, params): |
|---|
| 259 | room = params['room'] |
|---|
| 260 | conf = ConferenceHolder().getById(obj._conferenceID) |
|---|
| 261 | ExternalOperationsManager.execute(cls, "delete_"+str(cls.__class__)+str(room.getId()), cls.performOperation, 'delete', conf, room, room, conf) |
|---|
| 262 | |
|---|
| 263 | @classmethod |
|---|
| 264 | def addConference2Room(cls, obj, params): |
|---|
| 265 | room = params['room'] |
|---|
| 266 | conf = ConferenceHolder().getById(params['conf']) |
|---|
| 267 | #without the number added it would only send 1 mail, because for every new chat room it'd think that there has been a retry |
|---|
| 268 | ExternalOperationsManager.execute(cls, "add_"+str(cls.__class__)+str(room.getId()), cls.performOperation, 'create', conf, room, room, conf) |
|---|
| 269 | |
|---|
| 270 | @classmethod |
|---|
| 271 | def performOperation(cls, operation, conf, room, *args): |
|---|
| 272 | """ The 4 operations of this class are quite the same, so we pass the name of the operation, the conference they belong to, |
|---|
| 273 | the chat room for which we are creating the operation and, finally, a list of the arguments needed for the operation""" |
|---|
| 274 | |
|---|
| 275 | # get the list of users that will receive emails |
|---|
| 276 | pf = PluginFieldsWrapper('InstantMessaging', 'XMPP') |
|---|
| 277 | userList = list(pf.getOption('additionalEmails')) |
|---|
| 278 | if pf.getOption('sendMailNotifications'): |
|---|
| 279 | userList.extend( [user.getEmail() for user in pf.getOption('admins')] ) |
|---|
| 280 | if not len(userList) is 0: |
|---|
| 281 | try: |
|---|
| 282 | cn = ChatroomsNotification(room, userList) |
|---|
| 283 | ContextManager.get('mailHelper').newMail(GenericMailer.sendAndLog, getattr(cn, operation)(*args), \ |
|---|
| 284 | conf, \ |
|---|
| 285 | "MaKaC/plugins/InstantMessaging/XMPP/components.py", \ |
|---|
| 286 | room.getOwner()) |
|---|
| 287 | except Exception, e: |
|---|
| 288 | raise ServiceError(message=_('There was an error while contacting the mail server. No notifications were sent: %s'%e)) |
|---|
| 289 | |
|---|
| 290 | |
|---|
| 291 | class ChatroomsNotification(GenericNotification): |
|---|
| 292 | """ Template to build an email notification for a given chat room. """ |
|---|
| 293 | |
|---|
| 294 | def __init__(self, room, userList): |
|---|
| 295 | GenericNotification.__init__(self) |
|---|
| 296 | self.setFromAddr("Indico Mailer<%s>"%HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()) |
|---|
| 297 | self.setToList(userList) |
|---|
| 298 | |
|---|
| 299 | def create(self, room, conference=None): |
|---|
| 300 | """ If conference is None it means we're creating the chat room for the first time |
|---|
| 301 | if not it means we're re-using an existing chat room """ |
|---|
| 302 | if conference is None: |
|---|
| 303 | conference = room.getConferences().values()[0] |
|---|
| 304 | self.setSubject("[Indico] [IM] Chat room created in conference %s: %s" % (conference.getId(), room.getTitle())) |
|---|
| 305 | self.setBody(""" Dear Indico user, |
|---|
| 306 | The chat room with name %s has been successfully created in the conference %s (id: %s) |
|---|
| 307 | by the user %s. |
|---|
| 308 | |
|---|
| 309 | Thank you for using our system.""" %(room.getTitle(), conference.getTitle(), conference.getId(), room.getOwner().getFullName())) |
|---|
| 310 | return self |
|---|
| 311 | |
|---|
| 312 | def edit(self, room, conference): |
|---|
| 313 | self.setSubject("[Indico] [IM] Chat room edited in conference %s: %s" % (conference.getId(), room.getTitle())) |
|---|
| 314 | self.setBody(""" Dear Indico user, |
|---|
| 315 | The chat room with name %s has been successfully edited in the conference %s (id: %s) |
|---|
| 316 | by the user %s. |
|---|
| 317 | |
|---|
| 318 | Thank you for using our system.""" %(room.getTitle(), conference.getTitle(), conference.getId(), room.getOwner().getFullName())) |
|---|
| 319 | return self |
|---|
| 320 | |
|---|
| 321 | def delete(self, room, conference): |
|---|
| 322 | self.setSubject("[Indico] [IM] Chat room deleted in conference %s: %s" % (conference.getId(), room.getTitle())) |
|---|
| 323 | self.setBody(""" Dear Indico user, |
|---|
| 324 | The chat room with name %s has been successfully deleted from the conference %s (id: %s) |
|---|
| 325 | by the user %s. |
|---|
| 326 | |
|---|
| 327 | Thank you for using our system.""" %(room.getTitle(), conference.getTitle(), conference.getId(), room.getOwner().getFullName())) |
|---|
| 328 | return self |
|---|