Changeset 2f5998 in indico


Ignore:
Timestamp:
01/18/11 19:53:45 (2 years ago)
Author:
Jose Benito <jose.benito.gonzalez@…>
Branches:
master, burotel, hello-world-walkthrough, ipv6, v0.98-series, v0.98.2, v0.98.3, v0.98b1, v0.98b2, v0.99, 051b2622c51afb171a1dedb46a0df4fbb0cbd02e, d9941f8582b36b24821a11ea5ba16fda6a457fb1
Children:
1845d0
Parents:
869ccc
git-author:
Cesar Munoz Orena <cesar.munoz.orena@…> (12/15/10 17:27:01)
git-committer:
Jose Benito <jose.benito.gonzalez@…> (01/18/11 19:53:45)
Message:

[IMP] New link system and internationalization

  • Some strings were not calling the internationalization function.
  • The new system to generate links is fully working now.
  • Also, the file code.py that has to be put along with Jappix has been added to the project.
  • The refresh button was not working because of the change to sleekxmpp 1.0
  • In the mailer that sends mails when re-using chat rooms a random number was being added at the end, but if there was a retry emails would be sent more times than expected. The random number has been changed by the chat room ID.
  • Dependency checking for plugins
  • Added "unusable" state to plugins/types;
Files:
2 added
25 edited

Legend:

Unmodified
Added
Removed
  • bin/migration/migrate_0.97_0.98.py

    r43f6a0 r2f5998  
    5454 
    5555 
     56def runPluginMigration(): 
     57 
     58    # TODO: for each Plugin/PluginType, add __notUsableReason attribute (default None) 
     59 
    5660def main(): 
    5761    runTaskMigration() 
  • indico/MaKaC/common/Configuration.py

    ra755c1 r2f5998  
    160160                 "enabledSection": "enabledSection.png", 
    161161                 "disabledSection": "disabledSection.png", 
     162                 "greyedOutSection": "greyedOutSection.png", 
    162163                 "tick": "tick.png", 
    163164                 "cross": "cross.png", 
  • indico/MaKaC/common/TemplateExec.py

    r565750f r2f5998  
    3434import MaKaC.common.info as info 
    3535 
    36  
     36from MaKaC.common.logger import Logger 
    3737 
    3838 
     
    404404            try: open( ERROR_PATH + "/" + tplFilename + ".tpl.py", "w" ).write( pythonCode ) 
    405405            except: pass 
     406            Logger.get('tplexec').exception('Template error') 
    406407            raise 
    407408        except Exception, e: 
  • indico/MaKaC/plugins/Collaboration/actions.py

    r7abf81 r2f5998  
    5959        commonIndexes = {} 
    6060        plugins = self._pluginType.getPluginList(doSort = True, includeNonActive = True) 
    61         pluginNames = [p.getName() for p in plugins] 
     61        pluginNames = [p.getId() for p in plugins if p.isActive()] 
    6262 
    6363        for pluginName in pluginNames: 
  • indico/MaKaC/plugins/InstantMessaging/XMPP/bot.py

    rbc991f r2f5998  
    2424 
    2525from MaKaC.services.interface.rpc.common import ServiceError, NoReportError 
     26from MaKaC.i18n import _ 
    2627 
    2728class IndicoXMPPBotBase(object): 
     
    5556    def treatError(self, error, msg=''): 
    5657        """ Gets the response from the XMPP driver and, in case of error, returns the appropiate message""" 
    57         return {'error':error, 'reason':msg if msg!= '' else 'There was a problem while connecting our XMPP server. Please try again later'} 
     58        return {'error':error, 'reason':msg if msg!= '' else _('There was a problem while connecting our XMPP server. Please try again later')} 
    5859 
    5960    def run(self): 
  • indico/MaKaC/plugins/InstantMessaging/XMPP/components.py

    r869ccc r2f5998  
    2323from MaKaC.plugins.base import Observable, PluginsHolder 
    2424from MaKaC.plugins.util import PluginsWrapper, PluginFieldsWrapper 
    25 from MaKaC.plugins.helpers import DBHelpers, MailHelper, DesktopLinkGenerator, WebLinkGenerator, GeneralLinkGenerator 
     25from MaKaC.plugins.helpers import DBHelpers, MailHelper, GeneralLinkGenerator 
    2626from MaKaC.plugins.InstantMessaging.indexes import IndexByConf, IndexByCRName, IndexByID, IndexByUser 
    2727from MaKaC.plugins.InstantMessaging.Chatroom import XMPPChatroom 
     
    3939from MaKaC.services.interface.rpc.common import ServiceError, NoReportError 
    4040import zope.interface 
    41 import random 
    4241 
    4342 
     
    9998                out.writeTag("createdInLocalServer", chatroom.getCreatedInLocalServer()) 
    10099                out.openTag("links") 
    101                 if DesktopLinkGenerator(chatroom).isActive() or WebLinkGenerator(chatroom).isActive() or linksList.__len__() > 0: 
     100                if linksList.__len__() > 0: 
    102101                    out.writeTag("linksToShow", 'true') 
    103102                else: 
    104103                    out.writeTag("linksToShow", 'false') 
    105  
    106                 if DesktopLinkGenerator(chatroom).isActive(): 
    107                     out.writeTag("desktop", DesktopLinkGenerator(chatroom).generate()) 
    108                 else: 
    109                     out.writeTag("desktop", 'false') 
    110  
    111                 if WebLinkGenerator(chatroom).isActive(): 
    112                     out.writeTag("web", WebLinkGenerator(chatroom).generate()) 
    113                 else: 
    114                     out.writeTag("web", 'false') 
    115104 
    116105                for link in linksList: 
     
    282271        room = params['room'] 
    283272        conf = ConferenceHolder().getById(params['conf']) 
    284         #without the random number added it would only send 1 mail, because for every new chat room it'd think that there has been a retry 
    285         ExternalOperationsManager.execute(cls, "add_"+str(cls.__class__)+str(random.random()), cls.performOperation, 'create', conf, room, room, conf) 
     273        #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 
     274        ExternalOperationsManager.execute(cls, "add_"+str(cls.__class__)+str(room.getId()), cls.performOperation, 'create', conf, room, room, conf) 
    286275 
    287276    @classmethod 
  • indico/MaKaC/plugins/InstantMessaging/XMPP/handlers.py

    r869ccc r2f5998  
    2121 
    2222import urllib2, datetime, os, tempfile, stat 
     23from MaKaC.webinterface import urlHandlers 
    2324from MaKaC.plugins.InstantMessaging.Chatroom import XMPPChatroom 
    2425from MaKaC.plugins.InstantMessaging.handlers import ChatroomBase 
     
    3334from MaKaC.plugins import Observable 
    3435from MaKaC.plugins.util import PluginFieldsWrapper 
    35 from MaKaC.plugins.helpers import DBHelpers, MailHelper, DeleteLogLinkGenerator, LogLinkGenerator 
     36from MaKaC.plugins.helpers import DBHelpers, MailHelper, DeleteLogLinkGenerator, LogLinkGenerator, generateCustomLinks, generateLogLink, XMPPLogsActivated 
     37from MaKaC.i18n import _ 
    3638 
    3739from MaKaC.plugins.InstantMessaging.XMPP.bot import IndicoXMPPBotRoomExists, IndicoXMPPBotCreateRoom, IndicoXMPPBotEditRoom, IndicoXMPPBotDeleteRoom, IndicoXMPPBotGetPreferences 
     
    150152        return self.proccessAnswer(self._bot) 
    151153 
    152     def _executeExternalOperation(self, bot, operName, messageName): 
    153         """ we need the instance of the XMPP operation we're going to do, and also its name. 
    154             Finally, we'll need the name of the error message to show in case something happens""" 
    155  
     154def addLinks2FossilizedCR(fossilizedRoom, room): 
     155    """ Adds the neccesary links to retrieve logs and also to join the chat room through all the links specified by the user""" 
     156    # add links to join the room 
     157    generateCustomLinks(fossilizedRoom, room) 
     158    # add link to retrieve logs 
     159    generateLogLink(fossilizedRoom, room, room.getConference() if len(room.getConferences()) is 1 else room.getConference().values()[0]) 
     160 
     161    return True 
    156162 
    157163class CreateChatroom( XMPPChatroomService ): 
     
    199205 
    200206        ContextManager.get('mailHelper').sendMails() 
    201         return self._room.fossilize(tz=tz) 
     207        fossilizedRoom = self._room.fossilize(tz=tz) 
     208        addLinks2FossilizedCR(fossilizedRoom, self._room) 
     209 
     210        return fossilizedRoom 
    202211 
    203212 
     
    313322 
    314323        # make the log folder unaccessible in the future 
    315         url = DeleteLogLinkGenerator(self._room).generate() 
    316         req = urllib2.Request(url, None, {'Accept-Charset' : 'utf-8'}) 
    317         document = urllib2.urlopen(req) 
    318         Logger.get('InstantMessaging (XMPP-Indico server)').info("The room %s has been deleted by the user %s at %s hours" %(self._title, self._user.getName(), nowutc())) 
     324        if len(self._room.getConferences()) is 0: 
     325            # we only "delete" logs when there are no conferences associated with the chat room 
     326            url = DeleteLogLinkGenerator(self._room).generate() 
     327            req = urllib2.Request(url, None, {'Accept-Charset' : 'utf-8'}) 
     328            document = urllib2.urlopen(req) 
     329            Logger.get('InstantMessaging (XMPP-Indico server)').info("The room %s has been deleted by the user %s at %s hours" %(self._title, self._user.getName(), nowutc())) 
    319330 
    320331        ContextManager.get('mailHelper').sendMails() 
     
    345356        # this method will fill the self._bot._form attr 
    346357        self.roomPreferencesXMPP(self._botJID, self._botPass, self._room) 
    347  
    348358        #get the preferences and check updates 
    349         for preference in self._bot._form.fields: 
    350             if preference.var in fieldsToCheck.keys(): 
     359        for preference in self._bot._form.values['fields']: 
     360            if preference[0] in fieldsToCheck.keys(): 
    351361                #we execute setDescription or setPassword with the new value 
    352                 getattr(self._room, 'set'+ fieldsToCheck[preference.var])(preference.value.pop()) 
     362                getattr(self._room, 'set'+ fieldsToCheck[preference[0]])(preference[1].values['value']) 
    353363 
    354364        return self._room.fossilize() 
     
    389399                room.setConference(ConferenceHolder().getById(self._conference)) 
    390400                self._notify('addConference2Room', {'room': room, 'conf': self._conference}) 
    391                 rooms.append(room.fossilizeMultiConference(ConferenceHolder().getById(self._conference))) 
     401                fossilizedRoom = room.fossilizeMultiConference(ConferenceHolder().getById(self._conference)) 
     402                addLinks2FossilizedCR(fossilizedRoom, room) 
     403                rooms.append(fossilizedRoom) 
    392404        except NoReportError, e: 
    393405            Logger.get('InstantMessaging (XMPP-Indico server)').error("Error adding chat rooms. User: %s. Chat room: %s. Traceback: %s" %(self._aw.getUser().getFullName(), roomID, e)) 
  • indico/MaKaC/plugins/InstantMessaging/XMPP/options.py

    rc1435a r2f5998  
    5757                      "defaultValue": "<table width=\"100%\" align=\"center\" border=\"0\">\ 
    5858    <tr>\ 
    59         <td class=\"groupTitle\" style=\"padding-top:50px\">How to connect to the chat</td>\ 
     59        <td class=\"groupTitle\">How to connect to the chat</td>\ 
    6060    </tr>\ 
    6161    <tr>\ 
     
    7171</table>"}), 
    7272 
    73     ("joinDesktopClients", {"description": _("Show a link to join a chat room through desktop clients, like Pidgin"), 
     73    ("activateLogs", {"description": _("Make possible to see chat logs and attach them to the material \ 
     74    \ 
     75    REMEMBER, you will need to put in the Jappix dir the code.py file contained in the ext folder, inside the XMPP plugin."), 
    7476                            "type": bool, 
    75                             "defaultValue": True, 
    76                             "editable": True, 
    77                             "visible": True}), 
    78     ("joinWebClient", {"description": _("Show a link to join a chat room through our web client"), 
    79                             "type": bool, 
    80                             "defaultValue": True, 
     77                            "defaultValue": False, 
    8178                            "editable": True, 
    8279                            "visible": True}) 
     80#    ("joinWebClient", {"description": _("Show a link to join a chat room through our web client"), 
     81#                            "type": bool, 
     82#                            "defaultValue": True, 
     83#                            "editable": True, 
     84#                            "visible": True}) 
    8385] 
  • indico/MaKaC/plugins/InstantMessaging/__init__.py

    rc1435a r2f5998  
    2424__metadata__ = { 
    2525    'name': "Instant Messaging", 
    26     'description': _("Instant Messaging Plugins") 
     26    'description': _("Instant Messaging Plugins"), 
     27    'requires': ['sleekxmpp'] 
    2728    } 
  • indico/MaKaC/plugins/InstantMessaging/indexes.py

    rc1435a r2f5998  
    2525from persistent import Persistent 
    2626from MaKaC.plugins.util import PluginFieldsWrapper 
     27from MaKaC.i18n import _ 
    2728 
    2829class IMIndex(Persistent): 
  • indico/MaKaC/plugins/InstantMessaging/options.py

    r869ccc r2f5998  
    2424    ("customLinks", {"description": _("Create your own links to the chat rooms"), 
    2525                            "type": 'links', 
    26                             "defaultValue": [], 
     26                            "defaultValue": [{'name': 'your desktop client', 'structure': 'xmpp:*chatroom*@*host*?join'}], 
    2727                            "editable": True, 
    2828                            "visible": True}) 
  • indico/MaKaC/plugins/InstantMessaging/pages.py

    r869ccc r2f5998  
    3030from MaKaC.plugins.util import PluginFieldsWrapper 
    3131from MaKaC.webinterface.rh.conferenceModif import RHMaterialsShow 
    32 from MaKaC.plugins.helpers import GeneralLinkGenerator 
     32from MaKaC.plugins.helpers import generateCustomLinks, generateLogLink, XMPPLogsActivated 
     33from MaKaC.i18n import _ 
    3334 
    3435 
     
    113114            chatrooms = {} 
    114115        vars['links'] = {} 
     116 
    115117        for cr in chatrooms: 
    116118            vars['links'][cr.getId() ] = {} 
    117             if PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinDesktopClients'): 
    118                 vars['links'][cr.getId()]['web'] = DesktopLinkGenerator(cr).generate() 
    119             if PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinWebClient'): 
    120                 vars['links'][cr.getId()]['desktop'] = WebLinkGenerator(cr).generate() 
    121  
    122             vars['links'][cr.getId()]['logs'] = urlHandlers.UHConfModifChatSeeLogs.getURL(self._conf) 
    123             vars['links'][cr.getId()]['logs'].addParam('chatroom', cr.getId()) 
    124             vars['links'][cr.getId()]['logs'] = vars['links'][cr.getId()]['logs'].__str__() 
     119            generateCustomLinks(vars["links"][cr.getId()], cr) 
     120            generateLogLink(vars["links"][cr.getId()], cr, self._conf) 
    125121 
    126122        vars['DefaultServer'] = PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('chatServerHost') 
     
    129125        vars["tz"] = DisplayTZ(self._aw,self._conf).getDisplayTZ() 
    130126        vars["MaterialUrl"] = RHMaterialsShow._uh().getURL(self._conf).__str__() 
     127        vars["ShowLogsLink"] = XMPPLogsActivated() 
    131128 
    132129        return vars 
     
    151148            vars["Chatrooms"] = None 
    152149        vars["Links"] = {} 
    153         linksList = PluginsHolder().getPluginType('InstantMessaging').getOption('customLinks').getValue() 
    154150        for cr in vars["Chatrooms"]: 
    155151            vars["Links"][cr.getId()] = {} 
    156             for link in linksList: 
    157                 self.addLink(vars["Links"], link['name'], GeneralLinkGenerator(cr, link['structure']).generate(), cr.getId()) 
    158  
    159             #in case it's neccesary, generate for web clients and desktop clients 
    160             if DesktopLinkGenerator(cr).isActive(): 
    161                 self.addLink(vars["Links"], 'your desktop client', DesktopLinkGenerator(cr).generate(), cr.getId()) 
    162             if WebLinkGenerator(cr).isActive(): 
    163                 self.addLink(vars["Links"], 'web client', WebLinkGenerator(cr).generate(), cr.getId()) 
     152            generateCustomLinks(vars["Links"][cr.getId()], cr) 
    164153 
    165154        return vars 
    166155 
    167     def addLink(self, var, linkName, link, chatroomId): 
    168         """ Adds a link to the chat room if it's specified to do so""" 
    169         var[chatroomId][linkName] = {} 
    170         var[chatroomId][linkName]['name'] = linkName 
    171         var[chatroomId][linkName]['link'] = link 
    172  
  • indico/MaKaC/plugins/InstantMessaging/rh.py

    r4bd938 r2f5998  
    2727from MaKaC.webinterface.rh.conferenceDisplay import RHConferenceBaseDisplay 
    2828from MaKaC.webinterface.rh.conferenceModif import RHConferenceModifBase 
     29from MaKaC.i18n import _ 
    2930import urllib2 
    3031 
     
    8586        req = urllib2.Request(url, None, {'Accept-Charset' : 'utf-8'}) 
    8687        document = urllib2.urlopen(req).read() 
     88        if document is '': 
     89            return _('No logs were found for these dates') 
    8790        return document 
    8891 
  • indico/MaKaC/plugins/base.py

    r869ccc r2f5998  
    341341        ptypes = PluginLoader.getPluginTypeList() 
    342342 
    343         for pluginTypeName in ptypes: 
    344  
    345             if self.hasPluginType(pluginTypeName, mustBePresent=False, mustBeActive=False): 
    346                 pluginType = self.getPluginType(pluginTypeName) 
    347                 pluginType.setPresent(True) 
     343        for ptypeId in ptypes: 
     344 
     345            if self.hasPluginType(ptypeId, mustBePresent=False, mustBeActive=False): 
     346                ptype = self.getPluginType(ptypeId) 
     347                ptype.setPresent(True) 
    348348            else: 
    349                 pluginType = PluginType(pluginTypeName) 
    350                 self.add(pluginType) 
    351             pluginType.updateInfo() 
     349                ptype = PluginType(ptypeId) 
     350                self.add(ptype) 
     351 
     352            ptype.configureFromMetadata(processPluginMetadata(ptype.getModule())) 
     353            missingDeps = ptype.getModule().__missing_deps__ 
     354 
     355            # if there are dependencies missing, set as not usable 
     356            if len(missingDeps) > 0: 
     357                ptype.setUsable(False, reason = "Dependencies missing: %s " % \ 
     358                                missingDeps) 
     359                if ptype.isActive(): 
     360                    ptype.setActive(False) 
     361            else: 
     362                ptype.setUsable(True) 
     363 
     364            ptype.updateInfo() 
    352365 
    353366    def clearPluginInfo(self): 
     
    373386 
    374387    def hasPluginType(self, name, mustBePresent=True, mustBeActive=True): 
    375         """ Returns True if there is a PluginType with the given name. 
     388        """ 
     389        Returns True if there is a PluginType with the given name. 
    376390        """ 
    377391        if self.hasKey(name): 
     
    451465        self.__actions = {} 
    452466 
     467        self.__usable = False 
     468 
    453469    ############## actions related ############### 
    454470    @classmethod 
     
    665681        self._p_changed = 1 
    666682 
     683    def setUsable(self, value, reason = ''): 
     684        self.__notUsableReason = None if value else reason 
     685 
     686    def getNotUsableReason(self): 
     687        return self.__notUsableReason 
     688 
     689    def isUsable(self): 
     690        return self.__notUsableReason == None 
     691 
    667692 
    668693class PluginType (PluginBase): 
     
    671696    """ 
    672697 
    673     def __init__(self, name, description=None): 
     698    def __init__(self, ptypeId, description=None): 
    674699        """ Constructor 
    675700            -name: a string with the type name. e.g. "Collaboration" 
     
    680705        """ 
    681706        PluginBase.__init__(self) 
    682         self.__id = name 
    683         self.__name = name 
     707        self.__id = ptypeId 
     708        self.__name = ptypeId 
    684709        self.__description = description 
    685710        self.__present = True 
     
    688713        self._active = False 
    689714 
    690     def _updatePluginInfo(self, pluginModule, metadata): 
    691  
    692         #if it already existed, we mark it as present 
    693         pluginName = metadata['name'] 
    694  
    695         if self.hasPlugin(pluginName): 
    696             p = self.getPlugin(pluginName) 
     715    def configureFromMetadata(self, metadata): 
     716        self.__name = metadata['name'] 
     717 
     718    def _updatePluginInfo(self, pid, pluginModule, metadata): 
     719 
     720        if self.hasPlugin(pid): 
     721            p = self.getPlugin(pid) 
    697722            p.setDescription(metadata['description']) 
    698723            p.setPresent(True) 
     
    700725        #if it didn't exist, we create it 
    701726        else: 
    702             p = Plugin(pluginName, 
     727            p = Plugin(pid, 
    703728                       pluginModule.__name__, 
    704729                       self, 
     
    707732            self.addPlugin(p) 
    708733 
    709         p.setTestPlugin(metadata['testPlugin']) 
    710  
    711         if hasattr(pluginModule, "options") and \ 
    712                hasattr(pluginModule.options, "globalOptions"): 
    713             p.updateAllOptions(pluginModule.options.globalOptions) 
    714  
    715         if hasattr(pluginModule, "actions") and \ 
    716                hasattr(pluginModule.actions, "pluginActions"): 
    717             p.updateAllActions(pluginModule.actions.pluginActions) 
    718  
    719         self._updateComponentInfo(p, pluginModule) 
    720         self._updateRHMapInfo(p, pluginModule) 
    721         self._updateHandlerInfo(p, pluginModule) 
     734        p.configureFromMetadata(processPluginMetadata(p.getModule())) 
     735 
     736        missingDeps = p.getModule().__missing_deps__ 
     737 
     738        if len(missingDeps) > 0: 
     739            p.setUsable(False, reason = "Dependencies missing: %s " % missingDeps) 
     740 
     741            if p.isActive(): 
     742                p.setActive(False) 
     743 
     744        else: 
     745            p.setUsable(True) 
     746 
     747        # only set options, actions, components and handlers if plugin is active 
     748        if p.isActive(): 
     749            if hasattr(pluginModule, "options") and \ 
     750                   hasattr(pluginModule.options, "globalOptions"): 
     751                p.updateAllOptions(pluginModule.options.globalOptions) 
     752 
     753            if hasattr(pluginModule, "actions") and \ 
     754                   hasattr(pluginModule.actions, "pluginActions"): 
     755                p.updateAllActions(pluginModule.actions.pluginActions) 
     756 
     757            self._updateComponentInfo(p, pluginModule) 
     758            self._updateRHMapInfo(p, pluginModule) 
     759            self._updateHandlerInfo(p, pluginModule) 
    722760 
    723761    def updateInfo(self): 
     
    742780                continue 
    743781            else: 
    744                 self._updatePluginInfo(pluginModule, metadata) 
     782                self._updatePluginInfo(pluginModule.__name__.split('.')[-1], 
     783                                       pluginModule, 
     784                                       metadata) 
    745785 
    746786        ptypeModule = self.getModule() 
     
    753793 
    754794        # components, handlers, options and actions 
    755         self._updateComponentInfo(self, ptypeModule) 
    756         self._updateRHMapInfo(self, ptypeModule) 
    757         self._updateHandlerInfo(self, ptypeModule) 
    758         self.updateAllOptions(self._retrievePluginTypeOptions()) 
    759         self.updateAllActions(self._retrievePluginTypeActions()) 
     795        if self.isActive(): 
     796            # components, handlers, options and actions 
     797            self._updateComponentInfo(self, ptypeModule) 
     798            self._updateRHMapInfo(self, ptypeModule) 
     799            self._updateHandlerInfo(self, ptypeModule) 
     800            self.updateAllOptions(self._retrievePluginTypeOptions()) 
     801            self.updateAllActions(self._retrievePluginTypeActions()) 
    760802 
    761803 
     
    924966 
    925967 
    926     def __init__(self, name, moduleName, owner, description=None, active=False): 
     968    def __init__(self, pid, moduleName, owner, description=None, active=False): 
    927969        """ Constructor 
    928970            -moduleName: the module name corresponding to this plugin. e.g. "MaKaC.plugins.Collaboration.EVO" 
     
    938980        """ 
    939981        PluginBase.__init__(self) 
    940         self.__name = name 
     982        self.__name = pid 
     983        self.__id = pid 
    941984        self.__owner = owner 
    942985        self.__present = True 
     
    948991        self._storage = OOBTree() # storage..... 
    949992 
     993    def configureFromMetadata(self, metadata): 
     994        self.__name = metadata['name'] 
     995        self._testPlugin = metadata['testPlugin'] 
     996 
    950997    def getId(self): 
    951         return self.__name 
     998        return self.__id 
    952999 
    9531000    def getName(self): 
     
    9671014 
    9681015    def getModule(self): 
    969         return PluginLoader.getPluginByTypeAndName(self.getType(), self.getName()) 
     1016        return PluginLoader.getPluginByTypeAndId(self.getType(), self.getId()) 
    9701017 
    9711018    def getType(self): 
    972         return self.getOwner().getName() 
     1019        return self.getOwner().getId() 
    9731020 
    9741021    def hasDescription(self): 
     
    9861033    def setActive(self, value): 
    9871034        self.__active = value 
    988         if value is True: 
    989             #register the components related to the plugin 
     1035        if value: 
     1036            # register the components related to the plugin 
    9901037            PluginsHolder().getComponentsManager().addPlugin(self.getName()) 
    9911038        else: 
    992             #unregister the components related to the plugin 
     1039            # unregister the components related to the plugin 
    9931040            PluginsHolder().getComponentsManager().cleanPlugin(self.getName()) 
    9941041 
    9951042    def toggleActive(self): 
    996         if not self.isActive(): 
    997             self.__active = True 
    998             #register the components related to the plugin 
    999             PluginsHolder().getComponentsManager().addPlugin(self.getName()) 
    1000         else: 
    1001             self.__active = False 
    1002             #unregister the components related to the plugin 
    1003             PluginsHolder().getComponentsManager().cleanPlugin(self.getName()) 
    1004  
    1005     def setTestPlugin(self, testPlugin): 
    1006         self._testPlugin = testPlugin 
     1043        self.setActive(not self.isActive()) 
    10071044 
    10081045    def isTestPlugin(self): 
  • indico/MaKaC/plugins/helpers.py

    r869ccc r2f5998  
    2323from MaKaC.services.interface.rpc.common import ServiceError, NoReportError 
    2424from MaKaC.plugins.util import PluginFieldsWrapper 
     25from MaKaC.webinterface import urlHandlers 
    2526from MaKaC.plugins.InstantMessaging.indexes import CounterIndex, IndexByConf, IndexByID, IndexByUser 
     27from MaKaC.i18n import _ 
    2628 
    2729import string 
     
    132134    def generate(self): 
    133135        # we're not converting to lowercase because it might depend on the protocol 
    134         linkWithoutNick = string.replace(string.replace(self._structure, '/chatroom/', self._chatroom.getTitle()), '/host/', self._chatroom.getHost()) 
     136        linkWithoutNick = string.replace(string.replace(self._structure, '*chatroom*', self._chatroom.getTitle()), '*host*', self._chatroom.getHost()) 
    135137        if self._nick: 
    136             return string.replace(linkWithoutNick, '/nickname/', self._nick) 
     138            return string.replace(linkWithoutNick, '*nickname*', self._nick) 
    137139        else: 
    138140            return linkWithoutNick 
     141 
     142def generateCustomLinks(var, chatroom): 
     143    linkList = PluginFieldsWrapper('InstantMessaging').getOption('customLinks') 
     144    # if some link type is specified 
     145    if linkList.__len__() > 0: 
     146        var['custom'] = [] 
     147        for link in linkList: 
     148            addLink(var['custom'], link['name'], GeneralLinkGenerator(chatroom, link['structure']).generate(), chatroom.getId()) 
     149 
     150def generateLogLink(var, chatroom, conf): 
     151    if XMPPLogsActivated(): 
     152        var['logs'] = urlHandlers.UHConfModifChatSeeLogs.getURL(conf) 
     153        var['logs'].addParam('chatroom', chatroom.getId()) 
     154        var['logs'] = var['logs'].__str__() 
     155 
     156def XMPPLogsActivated(): 
     157    return PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('activateLogs') 
     158 
     159def addLink(var, linkName, link, chatroomId): 
     160    """ Adds a link to the chat room if it's specified to do so""" 
     161    clink = {} 
     162    clink['name'] = linkName 
     163    clink['link'] = link 
     164    var.append(clink) 
     165 
    139166 
    140167class WebLinkGenerator(LinkGenerator): 
     
    165192    def isActive(self): 
    166193        return PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinDesktopClients') 
     194 
    167195 
    168196class LogLinkGenerator(LinkGenerator): 
  • indico/MaKaC/plugins/loader.py

    rc1435a r2f5998  
    1818## along with CDS Indico; if not, write to the Free Software Foundation, Inc., 
    1919## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 
    20 import os 
    21 import sys 
     20 
     21""" 
     22This module defines the PluginLoader class, that is responsible for providing methods 
     23that allow loading plugins from the python path, and cataloguing them accordingly 
     24""" 
     25 
     26# system imports 
     27import os, sys, pkg_resources 
     28 
     29# database 
     30from persistent import Persistent 
     31 
     32# legacy MaKaC imports 
    2233from MaKaC.common.logger import Logger 
     34from MaKaC.errors import PluginError 
     35 
     36# package 
    2337from MaKaC.plugins.util import processPluginMetadata 
    2438 
    25 from persistent import Persistent 
    26  
    27 from MaKaC.errors import PluginError 
    2839 
    2940class ModuleLoadException(Exception): 
     
    3142 
    3243class PluginLoader(object): 
    33     """ Utility class that has methods to deal with plugins at low level. 
    34  
    35         There is an important class variable "_pluginsLoaded" that will store if the plugins have already been loaded in this request. 
    36         Sometimes a single request will need information about the plugins multiple times, in this way they are only loaded once. 
    37         Ideally plugins should only be loaded once during the execution time of the Apache Server but different request don't 
    38         (normally) share memory so this variable will (normally) start with a value of False. 
    39         However sometimes it happens that the value of this variable can be True at the beginning of a request, and plugins are 
    40         already loaded into memory. It is not clear when this happens; probably it depends on the execution model of Apache 
    41         (prefork or multithread). Even with prefork, sometimes Apache (or mod_python) seems to recycle processes from one request 
    42         to the next and the plugins will be in memory at the beginning of a request. 
    43  
    44         Methods that should be used from outside: 
    45         -loadPlugins: explores the subfolder structure of the MaKaC/plugins folder and loads the plugins into memory. 
    46                       the modules are stored into the class attribute "pluginList" 
    47                       the dictionaries with the plugin options, if existant, are stored into the class attribute "pluginTypeOptions" 
    48                       the plugin descriptions, if existant, are stored into the class attribute "pluginTypeDescriptions" 
    49         -reloadPlugins: forces to reload the plugins if they are already in memory 
    50         -reloadPluginType: forces to reload plugins if they are already in memory, but only those of a given type (e.g. "epayment", "Collaboration"). 
    51         -getPluginByTypeAndName: given a type (e.g. "Collaboration"), and a name (e.g. "EVO"), a module object corresponding to that plugin is returned 
    52         -getPluginType: given a type (e.g. "Collaboration"), a module object corresponding to that plugin type is returned 
    53         -getTypeList: returns a list of strings with the plugin types (e.g. ["epayment", "Collaboration"] 
    5444    """ 
    55  
    56     # A dictionary where the keys are plugin type names (e.g. "Collaboration", "RoomBooking", 
     45    Loads the plugins/types from the source code. Execution of the contained methods 
     46    should be avoided (currently invoked manually), as it is naturally slow. 
     47 
     48    TODO: Use setuptools extension points? 
     49    """ 
     50 
     51    # A dictionary where the keys are plugin type names 
    5752    # and the values are modules (e.g. MaKaC.plugins.Collaboration) 
    58     pluginTypeModules = {} 
    59  
    60     # A dictionary where the keys are plugin type names (e.g. "Collaboration", "RoomBooking", 
    61     # and the values are also dictionaries. 
    62     # These second-level dictionaries have plugin names as keys (e.g. "EVO", "Vidyo"), 
    63     # and python module objects as values 
    64     pluginModules = {} 
    65  
    66     pluginTypesLoaded = set() 
    67  
    68     pluginsDir = os.path.abspath(sys.modules["MaKaC.plugins"].__path__[0]) 
     53    _ptypeModules = {} 
     54 
     55    # A dictionary where the keys are plugin type names 
     56    # and the values are plugin module dictionaries (plugin_name:module). 
     57    _pmodules = {} 
     58 
     59    _ptypesLoaded = set() 
     60    _pluginsDir = os.path.abspath(sys.modules["MaKaC.plugins"].__path__[0]) 
    6961 
    7062    @classmethod 
    7163    def loadPlugins(cls): 
    72         """ Explores the subfolder structure of the MaKaC/plugins folder and loads the plugins into memory. 
    73             The modules are stored into the class attribute "pluginModules" 
    74             The dictionaries with the plugin options, if existant, are stored into the class attribute "pluginTypeOptions" 
    75             The plugin descriptions, if existant, are stored into the class attribute "pluginTypeDescriptions" 
     64        """ 
     65        Explores the subfolder structure of the MaKaC/plugins folder and loads the 
     66        plugins into memory. 
    7667        """ 
    7768 
    7869        #we loop through all the files and folders of indico/MaKaC/plugins/ 
    79         for itemName in os.listdir(cls.pluginsDir): 
     70        for itemName in os.listdir(cls._pluginsDir): 
    8071            #we only go deeper for folders 
    81             if os.path.isdir(os.path.join(cls.pluginsDir, itemName)): 
    82                 if not itemName in cls.pluginTypesLoaded: 
     72            if os.path.isdir(os.path.join(cls._pluginsDir, itemName)): 
     73                if not itemName in cls._ptypesLoaded: 
    8374                    cls.loadPluginType(itemName) 
    84                     cls.pluginTypesLoaded.add(itemName) 
     75                    cls._ptypesLoaded.add(itemName) 
    8576 
    8677    @classmethod 
    8778    def reloadPlugins(cls): 
    88         """ Forces to reload the plugins if they are already in memory 
    89         """ 
    90         cls.pluginTypeModules = {} 
    91         cls.pluginModules = {} 
    92         cls.pluginTypesLoaded = set() 
     79        """ 
     80        Forces the reload of all plugins if they are already in memory 
     81        """ 
     82        cls._ptypeModules = {} 
     83        cls._pmodules = {} 
     84        cls._ptypesLoaded = set() 
    9385        cls.loadPlugins() 
    9486 
    9587    @classmethod 
    96     def reloadPluginType(cls, pluginTypeName): 
    97         """ Forces to reload plugins if they are already in memory, but only those of a given type (e.g. "epayment", "Collaboration"). 
    98         """ 
    99         if pluginTypeName in cls.pluginTypesLoaded: 
    100             del cls.pluginTypeModules[pluginTypeName] 
    101             cls.pluginModules[pluginTypeName] = {} 
    102             cls.pluginTypesLoaded.remove(pluginTypeName) 
    103         cls.loadPluginType(pluginTypeName.translate(None, ' ')) 
    104         cls.pluginTypesLoaded.add(pluginTypeName) 
    105  
    106     @classmethod 
    107     def getPluginsByType(cls, pluginTypeName): 
    108         """ Given a plugin type name (e.g. "Collaboration"), a list of modules corresponding to the plugins of that type is returned. 
    109         """ 
    110         if not pluginTypeName in cls.pluginTypesLoaded: 
    111             cls.reloadPluginType(pluginTypeName) 
    112         return cls.pluginModules[pluginTypeName].values() 
    113  
    114     @classmethod 
    115     def getPluginType(cls, pluginTypeName): 
    116         """ Returns the module of a plugin type given its name 
    117             e.g. pluginTypeName = "Collaboration" will return the MaKaC.plugins.Collaboration module object 
    118         """ 
    119         if not pluginTypeName in cls.pluginTypesLoaded: 
    120             cls.reloadPluginType(pluginTypeName) 
    121  
    122         return cls.pluginTypeModules[pluginTypeName] 
    123  
    124     @classmethod 
    125     def getPluginByTypeAndName(cls, pluginTypeName, pluginName): 
    126         """ Returns the module of a plugin given the names of the plugin and its type, 
    127             e.g. pluginTypeName = "Collaboration" and pluginName = "EVO" 
    128         """ 
    129         if not pluginTypeName in cls.pluginTypesLoaded: 
    130             cls.reloadPluginType(pluginTypeName) 
    131  
    132         modulesDict = cls.pluginModules[pluginTypeName.translate(None, ' ')] 
     88    def reloadPluginType(cls, ptypeId): 
     89        """ 
     90        Forces the reload of all plugins in a type if they are already in memory 
     91        """ 
     92        if ptypeId in cls._ptypesLoaded: 
     93            del cls._ptypeModules[ptypeId] 
     94            cls._pmodules[ptypeId] = {} 
     95            cls._ptypesLoaded.remove(ptypeId) 
     96 
     97        cls.loadPluginType(ptypeId) 
     98        cls._ptypesLoaded.add(ptypeId) 
     99 
     100    @classmethod 
     101    def getPluginsByType(cls, ptypeId): 
     102        """ 
     103        Given a plugin type name (e.g. "Collaboration"), a list of modules 
     104        corresponding to the plugins of that type is returned. 
     105        """ 
     106        if not ptypeId in cls._ptypesLoaded: 
     107            cls.reloadPluginType(ptypeId) 
     108        return cls._pmodules[ptypeId].values() 
     109 
     110    @classmethod 
     111    def getPluginType(cls, ptypeId): 
     112        """ 
     113        Returns the module object of a plugin type given its name 
     114        """ 
     115        if not ptypeId in cls._ptypesLoaded: 
     116            cls.reloadPluginType(ptypeId) 
     117 
     118        return cls._ptypeModules[ptypeId] 
     119 
     120    @classmethod 
     121    def getPluginByTypeAndId(cls, ptypeId, pluginName): 
     122        """ 
     123        Returns the module object of a plugin given the names of the plugin and its 
     124        type 
     125        """ 
     126        if not ptypeId in cls._ptypesLoaded: 
     127            cls.reloadPluginType(ptypeId) 
     128 
     129        modulesDict = cls._pmodules[ptypeId] 
    133130 
    134131        if pluginName in modulesDict: 
    135132            return modulesDict[pluginName] 
    136133        else: 
    137             raise PluginError("Tried to get a plugin of the type " + pluginTypeName + " with name " + pluginName + " but there is no plugin called " + pluginName) 
     134            raise PluginError("Tried to get a plugin of the type %s with name %s " 
     135                              "but there is no such plugin" % (ptypeId, 
     136                                                               pluginName)) 
    138137 
    139138    @classmethod 
    140139    def getPluginTypeList(cls): 
    141         """ Returns a list of strings with the plugin types (e.g. ["epayment", "Collaboration"] 
     140        """ 
     141        Returns a list of strings with the plugin types (names) 
    142142        """ 
    143143        cls.loadPlugins() 
    144         return list(cls.pluginTypesLoaded) 
     144        return list(cls._ptypesLoaded) 
    145145 
    146146    @classmethod 
    147147    def importName(cls, moduleName, name): 
    148         """ Import a named object from a module in the context of this function, 
    149             which means you should use fully qualified module paths. 
    150  
    151             Return None on failure. 
     148        """ 
     149        Import a named object from a module in the context of this function, 
     150        which means you should use fully qualified module paths. 
    152151        """ 
    153152 
     
    157156        except: 
    158157            Logger.get('plugins.loader').exception( 
    159                 "Syntax error loading %s ('%s')" % (moduleName, 
    160                                                     name)) 
     158                "Error loading %s ('%s')" % (moduleName, 
     159                                             name)) 
    161160            raise ModuleLoadException("Impossible to load %s ('%s')" % \ 
    162161                                      (moduleName, name)) 
     
    168167 
    169168    @classmethod 
    170     def loadPluginType(cls, pluginTypeName): 
    171  
    172         #we load the plugin type module 
     169    def _checkSetuptoolsDependencies(cls, deplist, name): 
     170        """ 
     171        Checks the dependencies for a given plugin/type, using setuptools 
     172        """ 
     173        missing = [] 
     174 
     175        for dep in deplist: 
     176            try: 
     177                pkg_resources.require(dep) 
     178            except pkg_resources.DistributionNotFound: 
     179                Logger.get('plugins.loader').warning("Requirement '%s' not met for %s" % 
     180                                                     (dep, name)) 
     181                missing.append(dep) 
     182 
     183        return missing 
     184 
     185    @classmethod 
     186    def loadPluginType(cls, ptypeId): 
     187        """ 
     188        Loads a plugin type, going through its source tree and loading each plugin 
     189        as well. 
     190        """ 
     191 
     192        # we load the plugin type module 
    173193        try: 
    174             pluginTypeModule = cls.importName("MaKaC.plugins", pluginTypeName) 
    175         except ImportError: 
    176             raise Exception("Tried to load the plugin type: %s but the module MaKaC.plugins.%s did not exist" % (pluginTypeName, pluginTypeName)) 
    177         except KeyError: 
    178             raise Exception("Tried to load the plugin type: %s but the module MaKaC.plugins.%s did not exist" % (pluginTypeName, pluginTypeName)) 
    179  
    180         # we build the package name of a plugin type, e.g. MaKaC.plugins.Collaboration 
    181         pluginTypePackageName = "MaKaC.plugins.%s" % pluginTypeName 
    182         metadata = processPluginMetadata(pluginTypeModule) 
    183  
    184         #we check that the plugin type does not have an "ignore" attribute 
     194            ptypeModule = cls.importName("MaKaC.plugins", ptypeId) 
     195        except (ImportError, KeyError): 
     196            raise Exception("Tried to load the plugin type: %s but the module " 
     197                            "MaKaC.plugins.%s did not exist" % (ptypeId, 
     198                                                                ptypeId)) 
     199 
     200        metadata = processPluginMetadata(ptypeModule) 
     201 
     202        # check if the plugin should be ignored 
    185203        if metadata['ignore']: 
    186204            # stop loading here! 
    187205            return 
    188206 
    189         #if ignore == False, we store the plugin type module in cls.pluginTypeModules 
    190         cls.pluginTypeModules[pluginTypeName] = pluginTypeModule 
    191  
    192         # absolute path of a plugin type folder, e.g. /xxxxx/MaKaC/plugins/Collaboration/ 
    193         pluginTypePath = os.path.join(cls.pluginsDir, pluginTypeName) 
    194  
    195         #we loop through all the files and folders of the plugin type folder 
    196         for itemName in os.listdir(pluginTypePath): 
    197  
    198             # we strip the extension from the item name 
    199             # splitext returns a tuple (name, file extension). Ex: ("conference", ".py") 
    200             # if no extension, the 2nd element of the tuple will be an empty string 
    201             itemName, ext = os.path.splitext(itemName) 
    202             # case where we found a folder, i.e. a plugin folder (e.g. /xxxx/MaKaC/plugins/Collaboration/EVO/) 
    203             if os.path.isdir(os.path.join(cls.pluginsDir, pluginTypeName, itemName)): 
    204  
    205                 # we attempt to import the folder as a module. This will only work if there's an __init__.py inside the folder 
    206                 try: 
    207                     pluginModule = cls.importName(pluginTypePackageName, itemName) 
    208  
    209                 except ImportError: 
    210                     raise Exception("Tried to load the plugin  %s but the module MaKaC.plugins.%s.%s did not exist. Is there an __init__.py?" % (pluginTypeName, pluginTypeName, itemName)) 
    211                 except KeyError: 
    212                     raise Exception("Tried to load the plugin  %s but the module MaKaC.plugins.%s.%s did not exist. Is there an __init__.py?" % (pluginTypeName, pluginTypeName, itemName)) 
    213  
    214                 # we check that it was indeed a module. 
    215  
    216                 if pluginModule: 
    217                     pluginMetadata = processPluginMetadata(pluginModule) 
    218                 else: 
    219                     # Not a module? Nothing to do here... 
     207        #if ignore == False, we store the plugin type module in cls._ptypeModules 
     208        cls._ptypeModules[ptypeId] = ptypeModule 
     209 
     210        missingDeps = cls._checkSetuptoolsDependencies(metadata['requires'], 
     211                                                       ptypeId) 
     212 
     213        # save missing dependency info, so that the holder will know the module state 
     214        ptypeModule.__missing_deps__ = missingDeps 
     215 
     216        # check dependencies 
     217        if len(missingDeps) > 0: 
     218            # if some dependencies are not met, don't load submodules 
     219            cls._pmodules[ptypeId] = {} 
     220 
     221            Logger.get('plugins.loader').warning( 
     222                "Plugin type %s has unmet dependencies. It will be deactivated." % 
     223                ptypeId) 
     224            return 
     225        else: 
     226            # absolute path of a plugin type folder 
     227            ptypePath = os.path.join(cls._pluginsDir, ptypeId) 
     228 
     229            # we loop through all the files and folders of the plugin type folder 
     230            for itemName in os.listdir(ptypePath): 
     231 
     232                # we strip the extension from the item name 
     233                # splitext returns a tuple (name, file extension) 
     234                cls._loadPluginFromDir(ptypePath, ptypeId, ptypeModule, itemName) 
     235 
     236    @classmethod 
     237    def _loadPluginFromDir(cls, ptypePath, ptypeId, ptypeModule, pid): 
     238        """ 
     239        Loads a possible plugin from a directory 
     240        """ 
     241 
     242        pid, ext = os.path.splitext(pid) 
     243 
     244        # in case where we found a folder, i.e. a plugin folder 
     245        if os.path.isdir(os.path.join(cls._pluginsDir, ptypeId, pid)): 
     246 
     247            # we attempt to import the folder as a module. 
     248            # This will only work if there's an __init__.py inside the folder 
     249            try: 
     250                pmodule = cls.importName(ptypeModule.__name__, pid) 
     251 
     252            except (ImportError, KeyError): 
     253                raise Exception("Tried to load the plugin  %s but the module " 
     254                                "MaKaC.plugins.%s.%s did not exist. " 
     255                                "Is there an __init__.py?" % 
     256                                (ptypeId, ptypeId, pid)) 
     257 
     258            # we check that it was indeed a module. 
     259            if pmodule: 
     260                pmetadata = processPluginMetadata(pmodule) 
     261            else: 
     262                # Not a module? Nothing to do here... 
     263                return 
     264 
     265            # If it was a module, we check that the "type" field in the metadata 
     266            # of the plugin corresponds to the plugin type we are currently processing 
     267            if pmetadata['type'] == ptypeId: 
     268 
     269                # if this is the first plugin for this plugin type 
     270                if not ptypeId in cls._pmodules: 
     271                    cls._pmodules[ptypeId] = {} 
     272 
     273                missingDeps = cls._checkSetuptoolsDependencies(pmetadata['requires'], 
     274                                                               pid) 
     275 
     276                # save missing dependency info, so that the holder will know the 
     277                # module state 
     278                pmodule.__missing_deps__ = missingDeps 
     279 
     280                # check dependencies 
     281                if len(missingDeps) > 0: 
     282                    # if some dependencies are not met, don't load submodules 
     283                    cls._pmodules[ptypeId][pid] = pmodule 
     284 
     285                    Logger.get('plugins.loader').warning( 
     286                        "Plugin %s has unmet dependencies. It will be deactivated." % 
     287                        pid) 
    220288                    return 
    221                 # If it was a module, we check that there is a 
    222                 # "pluginType" variable in the __init__.py of the plugin and that it corresponds 
    223                 # to the plugin type we are currently processing 
    224                 if pluginTypeName == pluginMetadata['type']: 
    225                     cls.pluginTypeModules[pluginTypeName].__dict__[itemName] = pluginModule 
    226  
    227                     #if this is the first plugin for this plugin type, we add 
    228                     #a new key to the cls.pluginModules dictionary 
    229                     if not pluginTypeName in cls.pluginModules.keys(): 
    230                         cls.pluginModules[pluginTypeName] = {} 
    231  
    232                     #we store the module inside the cls.pluginModules object 
    233                     #note: we do not use itemName (the name of the folder) and use instead 
    234                     #      the "pluginName" variable in the __init__.py file of the plugin, 
    235                     #      which is its "true" name 
    236                     cls.pluginModules[pluginTypeName][pluginMetadata['name']] = pluginModule 
    237  
    238                     #we build the path of the plugin 
    239                     pluginPath = os.path.join(pluginTypePath, itemName) 
    240  
    241                     cls.loadSubModules(pluginModule, pluginPath) 
    242                 else: 
    243                     Logger.get("plugins.loader").warning("Module of type %s inside %s" % 
    244                                                          (pluginMetadata['type'], 
    245                                                           pluginTypeName)) 
    246  
    247             elif ext == ".py" and itemName != "__init__": 
    248                 pluginTypeSubModule = cls.importName(pluginTypePackageName, itemName) 
    249  
    250                 if pluginTypeSubModule: 
    251                     cls.pluginTypeModules[pluginTypeName].__dict__[itemName] = pluginTypeSubModule 
     289 
     290 
     291                cls._ptypeModules[ptypeId].__dict__[pid] = pmodule 
     292 
     293                # we store the module inside the cls._pmodules object 
     294                cls._pmodules[ptypeId][pid] = pmodule 
     295 
     296                #we build the path of the plugin 
     297                pluginPath = os.path.join(ptypePath, pid) 
     298 
     299                cls.loadSubModules(pmodule, pluginPath) 
     300            else: 
     301                Logger.get("plugins.loader").warning("Module of type %s inside %s" % 
     302                                                     (pmetadata['type'], 
     303                                                      ptypeId)) 
     304 
     305        elif ext == ".py" and pid != "__init__": 
     306            ptypeSubModule = cls.importName(ptypeModule.__name__, pid) 
     307 
     308            if ptypeSubModule: 
     309                cls._ptypeModules[ptypeId].__dict__[pid] = ptypeSubModule 
    252310 
    253311 
    254312    @classmethod 
    255313    def loadSubModules(cls, module, modulePath): 
     314        """ 
     315        Loads the submodules of a plugin (recursively) 
     316        """ 
    256317 
    257318        #dictionary whose keys are submodule names, and whose values are module objects 
    258         #after finding the submodules, we will add them to the __dict__ of the module 
    259319        foundSubModules = {} 
    260320 
     
    263323 
    264324            # we strip the extension from the item name 
    265             # splitext returns a tuple (name, file extension). Ex: ("conference", ".py") 
    266             # if no extension, the 2nd element of the tuple will be an empty string 
    267325            itemName, ext = os.path.splitext(itemName) 
    268326 
    269             #if the item is a directory, we may have found a subpackage (also a submodule) 
     327            # if the item is a directory, we may have found a subpackage 
    270328            if os.path.isdir(os.path.join(modulePath, itemName)): 
    271329 
    272                 #this will return a module if the subdirectory has an __init__.py inside, otherwise will raise KeyError 
    273330                try: 
    274331                    subModule = cls.importName(module.__name__, itemName) 
    275332                except KeyError: 
    276                     #we hit a folder that is not a package, such 
    277                     #plugins are allowed to have those 
     333                    # we hit a folder that is not a package, such 
     334                    # plugins are allowed to have those 
    278335                    continue 
    279336 
    280337 
    281                 #we store the submodule in the foundSubModules dictionary 
     338                # we store the submodule in the foundSubModules dictionary 
    282339                foundSubModules[itemName] = subModule 
    283                 #we make a recursive call 
     340                # we make a recursive call 
    284341                cls.loadSubModules(subModule, os.path.join(modulePath, itemName)) 
    285342 
    286             #if the item is a .py file and not __init__, its a submodule that is not a package 
     343            # if the item is a .py file and not __init__, it's a submodule that is 
     344            # not a package 
    287345            elif ext == ".py" and itemName != "__init__": 
    288346 
    289                 #this should return a subModule, unless there has been an error during import 
    290  
     347                # this should return a subModule, unless there 
     348                # has been an error during import 
    291349                subModule = cls.importName(module.__name__, itemName) 
    292350                foundSubModules[itemName] = subModule 
    293351 
    294         #once we have found all the submodules, we make sure they are in the __dict__ of the module: 
     352        # once we have found all the submodules, we make sure they are in the 
     353        # __dict__ of the module: 
    295354        for subModuleName, subModule in foundSubModules.iteritems(): 
    296355            module.__dict__[subModuleName] = subModule 
    297             #also, if there is a "modules" variable in the __init__.py of the plugin, we store the submodules there 
    298             #(needed by epayment plugins, for example) 
     356 
     357            # also, if there is a "modules" variable in the __init__.py of the 
     358            # plugin, we store the submodules there 
     359            # (needed by legacy epayment modules) 
    299360            if hasattr(module, "modules"): 
    300361                module.modules[subModuleName] = subModule 
     
    302363 
    303364class GlobalPluginOptions(Persistent): 
    304     """ A class that stores global information about all plugins. 
     365    """ 
     366    A class that stores global information about all plugins. 
    305367    """ 
    306368 
  • indico/MaKaC/plugins/util.py

    rc1435a r2f5998  
    3131        'ignore': False, 
    3232        'visible': True, 
    33         'testPlugin': False 
     33        'testPlugin': False, 
     34        'requires': [] 
    3435        } 
    3536 
     
    4445 
    4546class PluginsWrapper(object): 
    46     def __init__(self, pluginType, plugin): 
     47    def __init__(self, pluginType, plugin=None): 
    4748        self._pluginType = pluginType 
    4849        self._plugin = plugin 
    4950        try: 
    5051            from MaKaC.plugins import PluginsHolder 
    51             self._plugin = PluginsHolder().getPluginType(pluginType).getPlugin(plugin) 
     52            if self._plugin: 
     53                self._plugin = PluginsHolder().getPluginType(pluginType).getPlugin(plugin) 
     54            else: 
     55                self._pluginType = PluginsHolder().getPluginType(pluginType) 
    5256        except Exception, e: 
    5357            Logger.get('Plugins').error("Exception while trying to access either the plugin type %s or the plugin %s: %s" % (pluginType, plugin, str(e))) 
     
    6064    """Provides a simple interface to access fields of a given plugin""" 
    6165 
    62     def __init__(self, pluginType, plugin): 
     66    def __init__(self, pluginType, plugin=None): 
    6367        PluginsWrapper.__init__(self, pluginType, plugin) 
    6468 
    6569    def getOption(self, optionName): 
    6670        try: 
    67             return self._plugin.getOption(optionName).getValue() 
     71            if self._plugin: 
     72                return self._plugin.getOption(optionName).getValue() 
     73            else: 
     74                return self._pluginType.getOption(optionName).getValue() 
    6875        except Exception, e: 
    6976            Logger.get('Plugins').error("Exception while trying to access the option %s in the plugin %s: %s" % (self._pluginType, self._plugin, str(e))) 
     
    7279    def getAttribute(self, attribute): 
    7380        try: 
    74             return getattr(self._plugin, attribute) 
     81            if self._plugin: 
     82                return getattr(self._plugin, attribute) 
     83            else: 
     84                return getattr(self._pluginType, attribute) 
    7585        except AttributeError: 
    7686            Logger.get('Plugins').error("No attribute %s in plugin %s" % (attribute, self._plugin)) 
  • indico/MaKaC/webinterface/stylesheets/include/indico.xsl

    r869ccc r2f5998  
    755755                        <span style="margin-left: 20px;"></span> 
    756756                        <span class="CRDisplayMoreInfo" id="CRMoreInfo{./id}">More Info</span> 
    757                         <span style="margin-left:8px;margin-right:8px;">|</span> 
    758757 
    759758                        <xsl:if test="./links/linksToShow != 'false'"> 
     759                            <span style="margin-left:8px;margin-right:8px;">|</span> 
    760760                            <span style="font-weight: bold;"><a id="joinLink{./id}" name="{./id}" class="dropDownMenu highlight" href="#">Join now!</a></span> 
    761761                        </xsl:if> 
     
    895895                                    <xsl:text disable-output-escaping="yes"><![CDATA['){]]></xsl:text> 
    896896 
    897                                                 <xsl:if test="./links/web != 'false'" disable-output-escaping="yes"> 
    898                                                 <xsl:text disable-output-escaping="yes"><![CDATA[ 
    899                                                     menuItems['Using web client'] =']]></xsl:text> <xsl:value-of select="./links/web" disable-output-escaping="yes"/> 
    900                                                     <xsl:text disable-output-escaping="yes"><![CDATA['; 
    901                                                 ]]></xsl:text> </xsl:if> 
    902  
    903                                                 <xsl:if test="./links/desktop != 'false'" disable-output-escaping="yes"> 
    904                                                 <xsl:text disable-output-escaping="yes"><![CDATA[ 
    905                                                     menuItems['Using your desktop client'] =']]></xsl:text> <xsl:value-of select="./links/desktop" disable-output-escaping="yes"/> 
    906                                                     <xsl:text disable-output-escaping="yes"><![CDATA['; 
    907                                                 ]]></xsl:text> </xsl:if> 
     897 
    908898 
    909899                                                <xsl:for-each select="./links/customLink"><xsl:text disable-output-escaping="yes"><![CDATA[ 
  • indico/MaKaC/webinterface/tpls/AdminPlugins.tpl

    r3eae79 r2f5998  
    3030                <tr> 
    3131                    <td> 
    32                         <% if plugin.isActive(): %> 
     32                        <% if not plugin.isUsable(): %> 
     33                            <img class="imglink" alt="<%= _("Not in usable state")%>" src="<%=Config.getInstance().getSystemIconURL( 'greyedOutSection' )%>"/> 
     34                        <% end %> 
     35                        <% elif plugin.isActive(): %> 
    3336                            <a href="<%=urlHandlers.UHAdminTogglePlugin.getURL(plugin)%>"> 
    3437                                <img class="imglink" alt="<%= _("Click to disable")%>" src="<%=Config.getInstance().getSystemIconURL( 'enabledSection' )%>"/> 
     
    4043                            </a> 
    4144                        <% end %> 
     45                        <% if not plugin.isUsable(): %> 
     46                            <%= plugin.getName() %> 
     47                            <small class="smallRed"> 
     48                                (<%= plugin.getNotUsableReason() %>) 
     49                            </small> 
     50                        <% end %> 
     51                        <% else: %> 
    4252                        <a href="<%=urlHandlers.UHAdminTogglePlugin.getURL(plugin)%>"> 
    4353                            <%= plugin.getName() %> 
    4454                        </a> 
     55                        <% end %> 
    4556                        <% if plugin.hasDescription(): %> 
    4657                            <span style="margin-left: 2em;"> 
  • indico/MaKaC/webinterface/tpls/AdminPluginsMainTab.tpl

    rc1435a r2f5998  
    5151                    <td> 
    5252                        <a href="<%=urlHandlers.UHAdminTogglePluginType.getURL(pluginType)%>"> 
    53                         <% if pluginType.isActive(): %> 
     53                        <% if not pluginType.isUsable(): %> 
     54                            <img class="imglink" alt="<%= _("Not in usable state")%>" src="<%=Config.getInstance().getSystemIconURL( 'greyedOutSection' )%>"/> 
     55                        <% end %> 
     56                        <% elif pluginType.isActive(): %> 
    5457                                <img class="imglink" alt="<%= _("Click to disable")%>" src="<%=Config.getInstance().getSystemIconURL( 'enabledSection' )%>"/> 
    5558                        <% end %> 
     
    5861                        <% end %> 
    5962                        </a> 
    60                         <a href="<%=urlHandlers.UHAdminTogglePluginType.getURL(pluginType)%>" onclick="return confirm('<%= _('This will reload all the plugins too. Do you want to continue?')%>');"> 
     63                        <% if not pluginType.isUsable(): %> 
    6164                            <%= pluginType.getName() %> 
    62                         </a> 
     65                            <small class="smallRed"> 
     66                                (<%= pluginType.getNotUsableReason() %>) 
     67                            </small> 
     68                        <% end %> 
     69                        <% else: %> 
     70                            <a href="<%=urlHandlers.UHAdminTogglePluginType.getURL(pluginType)%>" onclick="return confirm('<%= _('This will reload all the plugins too. Do you want to continue?')%>');"> 
     71                                <%= pluginType.getName() %> 
     72                            </a> 
     73                        <% end %> 
    6374                        <% if pluginType.hasDescription(): %> 
    6475                            <span style="margin-left: 2em;"> 
  • indico/MaKaC/webinterface/tpls/AdminPluginsOptionList.tpl

    r869ccc r2f5998  
    8080                            var linksTable = Html.table({style: {padding: pixels(10)}}, linksBody); 
    8181                            linksBody.append( Html.tr({style: {marginTop: pixels(10)}}, Html.td({style:{whiteSpace: "nowrap", fontWeight:"bold"}}, $T('Link name')), 
    82                                                                                         Html.td({style:{whiteSpace: "nowrap", fontWeight:"bold"}}, $T('Link structure'))) ); 
     82                                                                                        Html.td({style:{whiteSpace: "nowrap", fontWeight:"bold"}}, $T('URL'))) ); 
    8383                            each(<%= option.getValue() %>, function(link){ 
    84                                     var removeButton = Html.input("button", {style:{marginRight: pixels(5)}}, $T('Remove')); 
    85                                     var newRow = Html.tr({style: {marginTop: pixels(10)}}, Html.td({style: {marginRight: pixels(10), whiteSpace: "nowrap"}},link.name), 
    86                                                                                            Html.td({style: {marginRight: pixels(10), whiteSpace: "nowrap"}},link.structure), 
    87                                                                                            Html.td({style:{whiteSpace: "nowrap"}},removeButton)) 
    88                                     linksBody.append(newRow); 
    89                                     removeButton.observeClick(function(){ 
     84                                    var removeButton = Widget.link(command(function(){ 
    9085                                        var killProgress = IndicoUI.Dialogs.Util.progress($T("Creating new type of link...")); 
    9186                                        indicoRequest( 
     
    106101                                            } 
    107102                                        ); 
    108                                     }); 
     103                                    }, IndicoUI.Buttons.removeButton())); 
     104                                    var newRow = Html.tr({style: {marginTop: pixels(10)}}, Html.td({style: {marginRight: pixels(10), whiteSpace: "nowrap"}},link.name), 
     105                                                                                           Html.td({style: {marginRight: pixels(10), whiteSpace: "nowrap"}},link.structure), 
     106                                                                                           Html.td({style:{whiteSpace: "nowrap"}},removeButton)) 
     107                                    linksBody.append(newRow); 
     108 
     109 
    109110                                }); 
    110111                                $E('links<%=name%>').append(linksTable); 
     
    113114                            $E('links<%=name%>').append(Html.div({style: {marginTop: pixels(10), marginBottom: pixels(10), whiteSpace: "nowrap"}}, $T('No links created yet. Click in Add new link if you want to do so!'))); 
    114115                        } 
    115                         var addButton = Html.input("button", {style:{marginRight: pixels(5)}}, $T('Add new link')); 
     116                        var addButton = Html.input("button", {style:{marginLeft: pixels(100)}}, $T('Add new link')); 
    116117 
    117118                        addButton.observeClick(function() { 
    118                             var errorLabel=Html.label({style:{float: 'right', display: 'none'}, className: " invalid"}, 'Name already in use'); 
     119                            var errorLabel=Html.label({style:{float: 'right', display: 'none'}, className: " invalid"}, $T('Name already in use')); 
    119120                            var linkName = new AutocheckTextBox({name: 'name', id:"linkname"}, errorLabel); 
    120121                            var linkStructure = Html.input("text", {}); 
    121                             var linksPopup = new ConfirmPopupWithPM($T('Select the name of the link and its structure'), 
    122                                     IndicoUtil.createFormFromMap([ 
    123                                                                         [$T('Link name'), Html.div({}, linkName.draw(), errorLabel)], 
    124                                                                         [$T('Link structure'), Html.div({}, linkStructure)], 
    125                                                                         [Html.ul({style: {fontWeight: "bold"}},$T('The following patterns will be substituted:'), 
    126                                                                                                        Html.li({style: {fontWeight: "lighter"}},$T('/chatroom/ by the chat room name')), 
    127                                                                                                        Html.li({style: {fontWeight: "lighter"}},$T('/host/ by the specified host')), 
    128                                                                                                        Html.li({style: {fontWeight: "lighter"}},$T('/nickname/ by the nick chosen by the user.')))] 
    129                                                                                                        ]), 
     122                            var div = Html.div({},IndicoUtil.createFormFromMap([ 
     123                                                                    [$T('Link name'), Html.div({}, linkName.draw(), errorLabel)], 
     124                                                                    [$T('URL'), Html.div({}, linkStructure)]]), 
     125                                                  Html.div({}, 
     126                                    (Html.ul({style: {fontWeight: "bold"}},$T('In the URL field, the following patterns will be changed:'), 
     127                                            Html.li({style: {fontWeight: "lighter"}},$T('*chatroom* by the chat room name')), 
     128                                            Html.li({style: {fontWeight: "lighter"}},$T('*host* by the specified host')), 
     129                                            Html.li({style: {fontWeight: "lighter"}},$T('*nickname* by the nick chosen by the user.'))) 
     130                                            )), 
     131                                     Html.div({style:{color: "orange", fontSize: "smaller"}}, $T('Example: http://*host*/resource/?x=*chatroom*@conference.*host*?join'))); 
     132                            var linksPopup = new ConfirmPopupWithPM($T('Select the name of the link and its URL'), 
     133                                    div, 
    130134                                                                        function(value){ 
    131135                                                                            if(value){ 
  • indico/MaKaC/webinterface/tpls/ConfModifChat.tpl

    r474924 r2f5998  
    5151/* ------------------------------ GLOBAL VARIABLES ------------------------------- */ 
    5252 
    53 <% from MaKaC.plugins.util import PluginFieldsWrapper %> 
    54  
    55 var showDesktopLink = <%=jsonEncode( PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinDesktopClients') )%>; 
    56 var showWebLink = <%=jsonEncode( PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinWebClient') )%>; 
    5753var chatrooms = $L(<%= jsonEncode(Chatrooms) %>); 
    5854var links = $O(<%= jsonEncode(links) %>); 
     55var showLogsLink = <%= jsonEncode(ShowLogsLink) %>; 
    5956 
    6057var defaultHost = <%= jsonEncode(DefaultServer) %>; 
  • indico/MaKaC/webinterface/tpls/ConferenceInstantMessaging.tpl

    r869ccc r2f5998  
    11<% declareTemplate(newTemplateStyle=True) %> 
    22 
    3 <% from MaKaC.plugins.helpers import WebLinkGenerator, DesktopLinkGenerator %> 
    43<% from MaKaC.plugins.util import PluginFieldsWrapper %> 
    5  
    6 <script type="text/javascript"> 
    7 var showDesktopLink = <%=jsonEncode( PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinDesktopClients') )%>; 
    8 var showWebLink = <%=jsonEncode( PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinWebClient') )%>; 
    9 </script> 
    104 
    115<table width="100%" align="center" border="0" cellpadding="5px"> 
     
    5044                    <td style="font-style:italic;"> - </td> 
    5145                <% end %> 
    52                 <% if PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinDesktopClients') or PluginFieldsWrapper('InstantMessaging', 'XMPP').getOption('joinWebClient'): %> 
     46                <% if PluginFieldsWrapper('InstantMessaging').getOption('customLinks').__len__() > 0: %> 
    5347                    <td style="font-weight: bold;" nowrap><a id="joinLink<%= cr.getId() %>" name = "<%= cr.getId() %>" class="dropDownMenu highlight" href="#"><%= _("Join now!")%></a></td> 
    5448                <% end %> 
     
    7973            var links = <%= Links %>; 
    8074            var crId = joinLink.dom.name; 
    81             each(links[crId], function(linkType){ 
     75            each(links[crId].custom, function(linkType){ 
    8276                menuItems['Using ' + linkType.name] = linkType.link; 
    8377            }); 
  • indico/htdocs/css/Default.css

    r3b1e93 r2f5998  
    64206420} 
    64216421 
     6422.smallRed { 
     6423    font-size: 9px; 
     6424    color: #881122; 
     6425} 
     6426 
     6427 
    64226428tr.selectedItem td { 
    64236429    color: #000; 
  • indico/htdocs/js/indico/Plugins/InstantMessaging.js

    r869ccc r2f5998  
    163163                                        showInfo[result.id] = true; // we initialize the show info boolean for this chatroom 
    164164                                        each(result, function(cr){ 
     165                                            links.set(cr.id, {}); 
     166                                            links.get(cr.id)['custom'] = cr.custom; 
     167                                            if(showLogsLink){ 
     168                                                links.get(cr.id)['logs'] = cr.logs; 
     169                                            } 
    165170                                            chatrooms.append(cr); 
    166171                                        }); 
     
    277282                                                disableCustomId(defaultHost); 
    278283                                             }); 
    279             var createCHRadioButtonLabel = Html.label({style:{fontWeight:"normal"}}, "Default "); 
     284            var createCHRadioButtonLabel = Html.label({style:{fontWeight:"normal"}}, $T("Default ")); 
    280285            createCHRadioButtonLabel.dom.htmlFor = "createCH"; 
    281286 
     
    285290                                                enableCustomId(customHost); 
    286291                                             }); 
    287             var defineCHRadioButtonLabel = Html.label({style:{fontWeight:"normal"}}, "Custom"); 
     292            var defineCHRadioButtonLabel = Html.label({style:{fontWeight:"normal"}}, $T("Custom")); 
    288293            defineCHRadioButtonLabel.dom.htmlFor = "defineCH"; 
    289294 
     
    291296            this.parameterManager.add(defineCHText, 'text', false); 
    292297 
    293             self.errorLabel=Html.label({style:{float: 'right', display: 'none'}, className: " invalid"}, 'Name already in use'); 
     298            self.errorLabel=Html.label({style:{float: 'right', display: 'none'}, className: " invalid"}, $T('Name already in use')); 
    294299 
    295300            self.crName = new AutocheckTextBox({style: {width: '300px'}, name: 'title', id:"CRname"}, self.errorLabel); 
     
    312317                           text.indexOf('>') != -1 || 
    313318                           text.indexOf('@') != -1){ 
    314                             return Html.span({}, "You introduced an invalid character in the name, don't use spaces, \', \", &, /, :, <, > or @"); 
     319                            return Html.span({}, $T("You introduced an invalid character in the name, don't use spaces, \', \", &, /, :, <, > or @")); 
    315320                        } 
    316321                    }), self.errorLabel)], 
     
    416421                                    hideAllInfoRows(false); 
    417422                                    showInfo[result.id] = true; // we initialize the show info boolean for this chat room 
     423                                    links.set(result.id, {}); 
     424                                    links.get(result.id)['custom'] = result.custom; 
     425                                    if(showLogsLink){ 
     426                                        links.get(result.id)['logs'] = result.logs; 
     427                                    } 
    418428                                    chatrooms.append(result); 
    419429                                    showAllInfoRows(false); 
     
    479489        this.popupType = popupType; 
    480490        if (popupType === 'create') { 
    481             var title = " Chat room creation"; 
     491            var title = $T(" Chat room creation"); 
    482492        } else if (popupType === 'edit') { 
    483493            this.chatroom = chatroom; 
    484             var title = ' Chat room modification'; 
     494            var title = $T(' Chat room modification'); 
    485495        } 
    486496 
     
    732742                function() {checkStatus(chatroom);} , 
    733743                Html.img({ 
    734                     alt: "Refresh chat room data", 
    735                     title: "Refresh chat room data", 
     744                    alt: $T("Refresh chat room data"), 
     745                    title: $T("Refresh chat room data"), 
    736746                    src: imageSrc("reload"), 
    737747                    style: { 
     
    744754        var checkStatusButton = 
    745755                Html.img({ 
    746                     alt: "Refresh not available in external servers", 
    747                     title: "Refresh not available in external servers", 
     756                    alt: $T("Refresh not available in external servers"), 
     757                    title: $T("Refresh not available in external servers"), 
    748758                    src: imageSrc("reload_faded"), 
    749759                    style: { 
     
    755765    row.append(cellEditRemove); 
    756766 
    757     var joinNow = Html.td({id:"joinLink", name:"joinLink", className : "dropDownMenu highlight", style:{fontWeight: "bold", whiteSpace: "nowrap"}}, Html.a({href: "#"}, $T("Join now!")) ); 
    758     row.append(joinNow); 
    759     showLinkMenu(joinNow, chatroom); 
    760  
    761     if(chatroom.createdInLocalServer && links.get(chatroom.id)){ 
    762         var logs = Html.td({id:"logsLink", name:"logsLink", className : "dropDownMenu highlight", style:{fontWeight: "bold", whiteSpace: "nowrap"}}, " | ", Html.a({href: "#"}, $T("Logs")) ); 
     767    if(links.get(chatroom.id).custom){ 
     768        var joinNow = Html.td({id:"joinLink", name:"joinLink", className : "dropDownMenu highlight", style:{fontWeight: "bold", whiteSpace: "nowrap"}}, Html.a({href: "#"}, $T("Join now!")) ); 
     769        row.append(joinNow); 
     770        showLinkMenu(joinNow, chatroom); 
     771    } 
     772 
     773    if(links.get(chatroom.id).custom && chatroom.createdInLocalServer && links.get(chatroom.id) && showLogsLink){ 
     774        var sepBar = Html.td({style:{fontWeight: "bold", whiteSpace: "nowrap"}}, " | "); 
     775        row.append(sepBar); 
     776    } 
     777 
     778    if(chatroom.createdInLocalServer && links.get(chatroom.id) && showLogsLink){ 
     779        var logs = Html.td({id:"logsLink", name:"logsLink", className : "dropDownMenu highlight", style:{fontWeight: "bold", whiteSpace: "nowrap"}}, Html.a({href: "#"}, $T("Logs")) ); 
    763780        row.append(logs); 
    764781        showLogOptions(logs, chatroom); 
     
    780797            } 
    781798            var menuItems = {}; 
    782             if (showDesktopLink){ 
    783                 menuItems['Using web client'] = links.get(chatroom.id).desktop; 
    784             } 
    785             if (showWebLink){ 
    786                 menuItems['Using your desktop client'] = links.get(chatroom.id).web; 
    787             } 
     799 
     800            each(links.get(chatroom.id).custom, function(linkType){ 
     801                menuItems['Using ' + linkType.name] = linkType.link; 
     802            }); 
    788803            joinMenu = new PopupMenu(menuItems, [element], 'categoryDisplayPopupList'); 
    789804            var pos = element.getAbsolutePosition(); 
     
    851866            var form = createBaseForm(); 
    852867 
    853             menuItems['See logs'] = new LogPopup($T('Select the dates to retrieve the logs'), 
     868            menuItems[$T('See logs')] = new LogPopup($T('Select the dates to retrieve the logs'), 
    854869                                                 form.content, 
    855870                                                 function(value){ 
     
    878893                                      ); 
    879894            var requestOk = false; 
    880             menuItems['Attach logs to event material'] = new LogPopup($T('Select the name that logs will have in the material section'), 
     895            menuItems[$T('Attach logs to event material')] = new LogPopup($T('Select the name that logs will have in the material section'), 
    881896                                                                      materialContent, 
    882897                                                                      function(value){ 
     
    891906                                                                                              confId: conferenceID, 
    892907                                                                                              crId: chatroom.id, 
    893                                                                                               sdate: form2.sdate.get()?form.sdate.get().replace(/\//g,"-"):null, 
    894                                                                                               edate: form2.edate.get()?form.edate.get().replace(/\//g,"-"):null, 
     908                                                                                              sdate: form2.sdate.get()?form2.sdate.get().replace(/\//g,"-"):null, 
     909                                                                                              edate: form2.edate.get()?form2.edate.get().replace(/\//g,"-"):null, 
    895910                                                                                              getAll: form2.getall.get(), 
    896911                                                                                              forEvent: form2.forevent.get(), 
     
    10421057 */ 
    10431058var displayChatrooms = function() { 
    1044     var killProgress = IndicoUI.Dialogs.Util.progress("Loading list of chatrooms..."); 
     1059    var killProgress = IndicoUI.Dialogs.Util.progress($T("Loading list of chatrooms...")); 
    10451060    // We bind the watchlist and the table through the template 
    10461061    bind.element($E("chatroomsTableBody"), chatrooms, chatroomTemplate); 
     
    10571072    if (length == 0) { 
    10581073        var cell = Html.td(); 
    1059         cell.set(Html.span('','Currently no chatrooms have been created')); 
     1074        cell.set(Html.span('',$T('Currently no chatrooms have been created'))); 
    10601075        headRow.set(cell); 
    10611076    } else { 
Note: See TracChangeset for help on using the changeset viewer.