Ejemplo n.º 1
0
class Day:

    def __init__( self, cal, day ):
        self._calendar = cal
        self._day = day
        self._confs = OOBTree()
        self._categs = []

    def _getCalendar( self ):
        return self._calendar

    def addConference(self,conf,categList,tz):
        for categ in categList:
            if categ not in self._categs:
                self._categs.append(categ)
        t = conf.getStartDate().astimezone(tz).time()
        if not self._confs.has_key(t):
            self._confs[t]=set()
        self._confs[t].add(conf)

    #sorting functions that caches calculated start times for every conf
    def _sortFunc(self, x,y):
        return cmp(self._cache[x], self._cache[y])

    def _calculateCache(self, confs):
        self._cache = {}
        for conf in confs:
            self._cache[conf] = conf.calculateDayStartTime(self._day).time()

    def getConferences(self):
        return [conf for confs in self._confs.values() for conf in confs]

    def getConferencesWithStartTime(self):
        res= [conf for confs in self._confs.values() for conf in confs]
        self._calculateCache(res)
        if res!=[]:
            res.sort(self._sortFunc)
        return [(event, self._cache[event]) for event in res]

    def getCategories(self):
        return self._categs

    def getWeekDay( self ):
        return calendar.weekday( self._day.year, \
                                    self._day.month, \
                                    self._day.day )

    def getDayNumber( self ):
        return self._day.day

    def getDate( self ):
        return self._day

    def __str__( self ):
        return "CalendarDay at '%s': %s --> %s"%(self._day, self._confs, self._categs)
Ejemplo n.º 2
0
class PersitentOOBTree(Persistent):
    """A persitent wrapper around a OOBTree"""

    def __init__(self):
        self._data = OOBTree()
        Persistent.__init__(self)
        self.__len = Length()

    @Lazy
    def _PersitentOOBTree__len(self):
        l = Length()
        ol = len(self._data)
        if ol > 0:
            l.change(ol)
        self._p_changed = True
        return l

    def __len__(self):
        return self.__len()

    def __setitem__(self, key, value):
        # make sure our lazy property gets set
        l = self.__len
        self._data[key] = value
        l.change(1)

    def __delitem__(self, key):
        # make sure our lazy property gets set
        l = self.__len
        del self._data[key]
        l.change(-1)

    def __iter__(self):
        return iter(self._data)

    def __getitem__(self, key):
        """See interface `IReadContainer`.
        """
        return self._data[key]

    def get(self, key, default=None):
        """See interface `IReadContainer`.
        """
        return self._data.get(key, default)

    def __contains__(self, key):
        """See interface `IReadContainer`.
        """
        return key in self._data

    has_key = __contains__

    def items(self, key=None):
        return self._data.items(key)

    def keys(self, key=None):
        return self._data.keys(key)

    def values(self, key=None):
        return self._data.values(key)
Ejemplo n.º 3
0
class TopicIndex(Persistent):

    implements(IInjection, ITopicQuerying)

    def __init__(self):
        self.clear()

    def clear(self):
        # mapping filter id -> filter
        self._filters = OOBTree()

    def addFilter(self, f ):
        """ Add filter 'f' with ID 'id' """
        self._filters[f.getId()] = f

    def delFilter(self, id):
        """ remove a filter given by its ID 'id' """
        del self._filters[id]

    def index_doc(self, docid, obj):
        """index an object"""

        for f in self._filters.values():
            f.index_doc(docid, obj)

    def unindex_doc(self, docid):
        """unindex an object"""

        for f in self._filters.values():
            f.unindex_doc(docid)

    def search(self, query, operator='and'):

        if isinstance(query, StringTypes): query = [query]
        if not isinstance(query, (TupleType, ListType)):
            raise TypeError('query argument must be a list/tuple of filter ids')

        f = {'and' : intersection, 'or' : union}[operator]
    
        rs = None
        for id in self._filters.keys():
            if id in query:
                docids = self._filters[id].getIds()
                rs = f(rs, docids)
            
        if rs:  return rs
        else: return IISet()
Ejemplo n.º 4
0
class Package(Persistent):
    pypi_url = 'http://pypi.python.org/pypi/{}/json'

    def __init__(self, name):
        self.__name__ = name
        self.name = name
        self.releases = OOBTree()

    def __getitem__(self, release_name):
        return self.releases[release_name]

    def __setitem__(self, key, value):
        key = format_key(key)
        self.releases[key] = value
        self.releases[key].__parent__ = self

    @classmethod
    @cache_region('pypi', 'get_last_remote_filename')
    def get_last_remote_version(cls, proxy, package_name):
        logger.debug('Not in cache')
        if not proxy:
            return None
        try:
            result = requests.get('http://pypi.python.org/pypi/{}/json'.format(package_name))
            if not result.status_code == 200:
                return None
            result = json.loads(result.content.decode('utf-8'))
            return result['info']['version']
        except ConnectionError:
            pass
        return None

    def repository_is_up_to_date(self, last_remote_release):
        if not last_remote_release:
            return True
        remote_version = parse_version(last_remote_release)

        local_versions = [release.version for release in self.releases.values()]
        for version in local_versions:
            if parse_version(version) >= remote_version:
                return True
        return False

    @classmethod
    def by_name(cls, name, request):
        root = repository_root_factory(request)
        return root[name] if name in root else None

    def get_last_release(self):
        max_version = max([parse_version(version) for version in self.releases.keys()])
        for version, release in self.releases.items():
            if parse_version(version) == max_version:
                return release
Ejemplo n.º 5
0
class Day:
    
    def __init__( self, cal, day ):
        self._calendar = cal
        self._day = day
        self._confs = OOBTree()
        self._categs = []

    def _getCalendar( self ):
        return self._calendar
        
    def addConference(self,conf,categList,tz):
        for categ in categList:
            if categ not in self._categs:
                self._categs.append(categ)
        t = conf.getStartDate().astimezone(tz).time()
        if not self._confs.has_key(t):
            self._confs[t]=sets.Set()
        self._confs[t].add(conf.getId())
        
    def getConferenceIds(self):
        res=[]
        for confs in self._confs.values():
            for conf in confs:
                res.append(conf)
        return res

    def getConferences(self):
        res=[]
        ch=conference.ConferenceHolder()
        for confId in self.getConferenceIds():
            res.append(ch.getById(confId))
        if res!=[]:
            res.sort(lambda x,y: cmp(x.calculateDayStartTime(self._day).time(),y.calculateDayStartTime(self._day).time()))
        return res

    def getCategories(self):
        return self._categs

    def getWeekDay( self ):
        return calendar.weekday( self._day.year, \
                                    self._day.month, \
                                    self._day.day )
                                    
    def getDayNumber( self ):
        return self._day.day

    def getDate( self ):
        return self._day

    def __str__( self ):
        return "CalendarDay at '%s': %s --> %s"%(self._day, self._confs, self._categs)
Ejemplo n.º 6
0
class ActivityUtil(object):
    """ See IActivityUtil """
    implements(IActivityUtil)
    
    def __init__(self):
        self._storage = OOBTree()

    def mark_activity_for(self, userid, meeting_uid, dt = None, **kw):
        assert isinstance(meeting_uid, basestring)
        kw['dt'] = dt and dt or utcnow()
        kw['userid'] = userid
        kw['m_uid'] = meeting_uid
        try:
            us = self._storage[meeting_uid][userid]
        except KeyError:
            if meeting_uid not in self._storage:
                self._storage[meeting_uid] = OOBTree()
            us = self._storage[meeting_uid][userid] = OOBTree()
        for (k, v) in kw.items():
            us[k] = v
        return kw.keys()

    def maybe_mark(self, context, request, dt = None, **kw):
        userid = authenticated_userid(request)
        if not userid:
            return
        if not has_permission(VIEW, context, request):
            return
        meeting = find_interface(context, IMeeting)
        if meeting:
            self.mark_activity_for(userid, meeting.uid, dt = dt, **kw)

    def latest_activity(self, meeting_uid, userid = None, limit = 5):
        if meeting_uid not in self._storage:
            return ()
        res = [v for (k, v) in self._storage[meeting_uid].items() if k != userid]
        res = sorted(res, key = lambda x: x['dt'], reverse = True)
        return limit and tuple(res[:limit]) or tuple(res)

    def latest_user_activity(self, userid, meeting_uid = None, limit = 5):
        if meeting_uid is not None:
            try:
                return tuple([self._storage[meeting_uid][userid]])
            except KeyError:
                return ()
        res = []
        for m in self._storage.values():
            if userid in m:
                res.append(m[userid])
        res = sorted(res, key = lambda x: x['dt'], reverse = True)
        return limit and tuple(res[:limit]) or tuple(res)
Ejemplo n.º 7
0
class Inbox(BTreeDictBase):

    username = None
    new_messages_count = 0

    def __init__(self, username):
        self.data = OOBTree()
        self.username = username

    def add_conversation(self, conversation):
        self[conversation.username] = conversation
        return conversation

    def __setitem__(self, key, conversation):
        if key != conversation.username:
            msg = 'conversation.username and key differ ({0}, {1})'
            raise KeyError(msg.format(conversation.username, key))

        if conversation.username == self.username:
            raise ValueError("You can't speak to yourself")

        verifyObject(IConversation, conversation)

        if key in self:
            raise KeyError('Conversation exists already')

        super(Inbox, self).__setitem__(conversation.username, conversation)
        self.update_new_messages_count(conversation.new_messages_count)
        return conversation

    def __delitem__(self, key):
        conversation = self[key]
        self.update_new_messages_count(conversation.new_messages_count * -1)
        super(Inbox, self).__delitem__(key)

    def get_conversations(self):
        return self.data.values()

    def is_blocked(self, username):
        # FIXME: not implemented
        return False

    def update_new_messages_count(self, difference):
        count = self.new_messages_count
        count = count + difference
        if count < 0:
            # FIXME: Error. Log?
            count = 0
        self.new_messages_count = count
class AttachmentStorage(Traversable, Persistent, Explicit):
    """ The attachment storage is a container for all attachments on content
        objects (that provide IAttachmentStoragable).

        All objects including non-folderish ones, can be adapted to store
        attachments, since attached objects are not stored inside the object,
        but inside this AttachmentStorage container (which is stored as an
        annotation on the object being attached to).
    """
    interface.implements(IAttachmentStorage, IHideFromBreadcrumbs)
    __allow_access_to_unprotected_subobjects__ = True

    def __init__(self, id="++attachments++default"):
        self.id = id
        if not shasattr(self, '_attachments'):
            self.init_storage()

    def __getitem__(self, id):
        """ """
        return self.get(id)

    def getId(self):
        """ Get the id of the storage. This is used to construct a URL.
        """
        return self.id

    def init_storage(self):
        self._attachments = OOBTree()

    def keys(self):
        return self._attachments.keys()

    def values(self):
        return [att.__of__(self) for att in
                self._attachments.values()]

    def get(self, id):
        return self._attachments.get(id).__of__(self)

    def add(self, attachment):
        if attachment.getId() in self._attachments:
            raise DuplicateIDError
        self._attachments[attachment.getId()] = attachment

    def remove(self, id):
        del self._attachments[id]
Ejemplo n.º 9
0
class Survey(Content, ContextACLMixin, LocalRolesMixin, TranslationMixin):
    type_title = _("Survey")
    type_name = "Survey"
    add_permission = ADD_SURVEY
    allow_anonymous_to_invite_themselves = False
#    allow_anonymous_to_start = False

    def __init__(self, **kw):
        self.tokens = OOBTree()
        super(Survey, self).__init__(**kw)

    def create_token(self, email, size = 15, hours = 0, overwrite = False):
        """ Create a survey invitation token."""
        if email not in self.tokens or (email in self.tokens and overwrite == True):
            token = None
            while token is None or token in self.tokens.values():
                token = Token(size = size, hours = hours)
            self.tokens[email] = token
        return self.tokens[email]

    def get_participants_data(self):
        """Returns the participants with statistics on the survey
        """
        participants = []
        for (email, uid) in self.tokens.items():
            participant = {} 
            participant['uid'] = uid
            participant['email'] = email
            response = 0
            questions = 0
            sections = [x for x in self.values() if ISurveySection.providedBy(x)]
            for section in sections:
                response += len(section.responses.get(uid, {}))
                questions += len(section.question_ids)
            if response != 0:
                participant['finished'] = Decimal(response) / Decimal(questions) * 100
            else:
                participant['finished'] = 0                
            participants.append(participant)
        return participants
Ejemplo n.º 10
0
class Channel(Commentable):
    """Channel class"""

    type_title = _('Channel')
    icon = 'icon novaideo-icon icon-idea'
    templates = {'default': 'novaideo:views/templates/channel_result.pt'}
    name = renamer()
    members = SharedMultipleProperty('members', 'following_channels')
    subject = SharedUniqueProperty('subject', 'channels')

    def __init__(self, **kwargs):
        super(Channel, self).__init__(**kwargs)
        self.set_data(kwargs)
        self._comments_at = OOBTree()

    def add_comment(self, comment):
        self._comments_at[comment.created_at] = get_oid(comment)

    def remove_comment(self, comment):
        self._comments_at.pop(comment.created_at)

    def get_comments_between(self, start, end):
        return list(self._comments_at.values(
            min=start, max=end))

    def get_subject(self, user=None):
        subject = self.subject
        return subject if subject else getattr(self, '__parent__', None)

    def get_title(self, user=None):
        title = getattr(self, 'title', '')
        if not title:
            return getattr(self.get_subject(user), 'title', None)

        return title

    def is_discuss(self):
        return self.subject.__class__.__name__.lower() == 'person'
Ejemplo n.º 11
0
   def test_range_seach(self):
       """
       """
       array = list(range(self.complexity))
       random.shuffle(array)
       d = {i: -i for i in array}
       t = OOBTree()
       t.update(d)
         
       # === dict ===
       st = time.clock()
       result1 = list()
       for k, v in d.items():
           if (self.lower <= k <= self.upper):
               result1.append(v)
       elapse1 = time.clock() - st
 
       # === bisearch ===
       sorted_keys = list(d.keys()) # sort the list of keys
       sorted_keys.sort()
       st = time.clock()
       lower_ind = bisect.bisect_left(sorted_keys, self.lower) # find the min index
       upper_ind = bisect.bisect_right(sorted_keys, self.upper) - 1 # find the max index
       result2 = list() # fetch item
       for ind in range(lower_ind, upper_ind+1):
           result2.append(d[sorted_keys[ind]])
       elapse2 = time.clock() - st
         
       # === tree ===
       st = time.clock()
       result3 = t.values(min=self.lower, max=self.upper, excludemin=False, excludemax=False)
       elapse3 = time.clock() - st
         
       print("results are:", result1, result2, list(result3))
       print("dict method = %.6f, bisearch = %.6f, tree = %.6f" % (elapse1, elapse2, elapse3))
       self.assertGreater(elapse1, elapse2)
       self.assertGreater(elapse2, elapse3)
Ejemplo n.º 12
0
class SinTool(UniqueObject, ActionProviderBase, SimpleItem):
    """ CMF Syndication Client  """

    id        = 'sin_tool'
    meta_type = 'CMFSin Syndication Tool'

    security = ClassSecurityInfo()

    _actions = [ActionInformation(
        id='newfeeds'
        , title='NewsFeeds'
        , action=Expression(text='string: ${portal_url}/sin_tool/sincfg')
        , condition=Expression(text='member')
        , permissions=(ManagePortal,)
        , category='portal_tabs'
        , visible=0
        )]

    manage_options=(
        ({ 'label'   : 'Config',
           'action'  : 'manage_configForm',
           },
         {  'label'  : 'Debug',
            'action' : 'manage_debugForm',
            },
         ) + SimpleItem.manage_options
        +  ActionProviderBase.manage_options
        )

    manage_debugForm = PageTemplateFile('www/debug', globals())
    manage_configForm = PageTemplateFile('www/config', globals())

    def __init__(self):
        self._reset()

    def _reset(self, config=None):
        self.maps     = OOBTree()
        self._v_data     = OOBTree()
        self.channels = OOBTree()
        self.config   = config

    security.declarePublic('Maps')
    def Maps(self):
        """ Available Maps """
        return self.maps.values()

    security.declarePublic('Channels')
    def Channels(self):
        """ Available Channels """
        return self.channels.values()

    security.declarePublic('Policies')
    def Policies(self):
        """ Available Policies """
        return listPolicies()

    def encode(self, parsed_data):
        # Get site encoding

        enc = 'iso8859-1'
        try:
            pp = getToolByName(self, 'portal_properties')
            enc = getattr(pp.site_properties, 'default_charset', 'iso8859-1')
        except (AttributeError, KeyError):
            pass
        info = parsed_data['channel']
        data = parsed_data['items']

#         encoding = parsed_data.get('encoding', 'ascii')
#         for key in ('description', 'tagline', 'title'):
#             if isinstance(info[key], basestring) and not isinstance(info[key], unicode):
#                 info[key] = udecode(info[key], encoding)
#         for r in data:
#             for key in ('description', 'source', 'summary', 'title'):
#                 if isinstance(r[key], basestring) and not isinstance(r[key], unicode):
#                     r[key] = udecode(r[key], encoding)
#         return info, data
        
        if info.has_key('title'):
            if type(info['title']) not in (UnicodeType, ):
                info['title'] = udecode(info['title']).encode(enc)
        if info.has_key('description'):
            if type(info['description']) not in (UnicodeType, ):
                info['description'] = udecode(info['description']).encode(enc)
        for r in data:
            if r.has_key('title'):
                if type(r['title']) not in (UnicodeType, ):
                    r['title'] = udecode(r['title']).encode(enc)
        return info, data

    def _update(self, channel, force=None):
        # Hard update of a channels feed -> data
        try:
            parser = lookupParser(channel.id)
            data = self.encode(parser(channel.uri))
            channel.update(data, force)
            # Lastly, we update the existing data
            # if everything worked
            if not hasattr(aq_base(self), '_v_data'):
                self._v_data = OOBTree()
            self._v_data[channel.id] = data
        except (IOError, OSError):
            channel.failed()

    security.declareProtected(View, 'updateChannel')
    def updateChannel(self, channel, force=None):
        if not isinstance(channel, Channel):
            channel = self.channels[channel]

        if force or channel.requireUpdate():
            self._update(channel, force)

    security.declareProtected(View, 'sin')
    def sin(self, map_name, force=0, max_size=None):
        """
        Returns the syndication info for a given mapping
        force    -- force a channel update
        max_size -- max size of result set (may be used in policy to calc pri)
        """

        if not hasattr(aq_base(self), '_v_data'):
            self._v_data = OOBTree()

        # With a fallback to channel for development
        map = self.maps.get(map_name)
        if not map:
            map = self.channels.get(map_name, None)
            if map is None:
                LOG('SinTool.sin', INFO, 'channel %s not found' % map_name)
                return []
            else:
                channels = (map, )
        else:
            channels = map.Channels()

        for ci in channels:
            enabled = ci['enabled']
            if not enabled: continue
            channel = ci['channel']
            self.updateChannel(channel, force)

# How safe is it to use threading inside Zope?
#             import threading
#             thread_name = "SinTool.Channel.%s" % channel.id
#             if not [t for t in threading.enumerate() if \
#                     t.getName() == thread_name]:
#                 threading.Thread(target=self.updateChannel, name=thread_name, \
#                                  args=(channel, force)).start()

        #Collect all the data for all the enabled channels now
        results = []
        links   = {}

        for ci in channels:
            enabled = ci['enabled']
            if not enabled: continue
            channel = ci['channel']
            priority = ci['priority']
            data = self._v_data.get(channel.id, None)
            final   = []

            if not data: continue
            # We remove dup links inline
            for link in data[1]:
                if not links.has_key(link['link']):
                    links[link['link']] = 1
                    final.append(link)
                #We don't cache skips now because the Map
                #doesn't keep any data, just the channel
                #relationship, if this is an issue
                #its easy enough to change

            data[0]['priority'] = priority
            results.append(data)

        policy = map.policy
        results = policy.order(results, max_size=max_size)

        return results

    security.declareProtected(ModifyPortalContent, 'addChannel')
    def addChannel(self, name, url, **kwargs):
        c = Channel(name, url, **kwargs)
        self.channels[name] = c

    security.declareProtected(ModifyPortalContent, 'addMap')
    def addMap(self, map, channels=[]):
        self.maps[map] = m = Map(map)
        for c in channels:
            c = self.channels[c]
            m.addChannel(c)
        return m

    security.declarePrivate('parse')
    def parse(self, file):
        if type(file) == type(''):
            file = StringIO(file)

        config = ConfigParser()
        config.readfp(file)

        s = 'channels'
        options = config.options(s)
        args = {}
        for o in options:
            uri =  config.get(s, o, raw=1)
            match = schedRe.match(uri)
            if match:
                uri = uri[match.end():]
                args['period']    = match.group('period')
                args['frequency'] = int(match.group('freq'))
            self.addChannel(o, uri, **args)
            args.clear()

        s = 'maps'
        options = config.options(s)
        for o in options:
            channels = config.get(s, o, raw=1)
            #We look for policy_name:x,y,z
            #if no policy is specified default is used
            idx = channels.find(':')
            policy = None
            if idx != -1:
                policy, channels = channels[:idx], channels[idx+1:]

            m = self.addMap(o)
            if policy:
                m.setPolicy(policy)

            channels = channels.split(',')
            for c in channels:
                c = c.strip()  # remove whitespace around delimitters
                if c.endswith(')'):
                    #look for channel(pri) format
                    idx = c.rfind('(')
                    if idx != -1:
                        pri = int(c[idx+1:-1])
                        c = c[:idx]
                else:
                    pri = 0

                c = self.channels[c]
                m.addChannel(c, priority=pri)

        file.seek(0)
        self.config = file.read()

    security.declarePrivate('load')
    def load(self, filename):
        """ Load a file into config """
        name = os.path.join(package_home(globals()), \
                            os.path.basename(filename))
        if not name.endswith('.cfg'):
            name += '.cfg'

        fp = open(name)
        self.parse(fp)

    security.declarePrivate('save')
    def save(self, filename):
        """ Write config into a file """
        name = os.path.join(package_home(globals()), \
                            os.path.basename(filename))
        if not name.endswith('.cfg'):
            name += '.cfg'

        fp = open(name, 'w')
        fp.write(self.config)
        fp.close()

    security.declareProtected(ModifyPortalContent, 'updateConfig')
    def updateConfig(self, config=None, REQUEST=None, *args):
        """Update the config using new info"""
        self.parse(config)

        if REQUEST:
            return REQUEST.RESPONSE.redirect(self.absolute_url() + \
                                             "/sincfg?portal_status_message=Config+Updated")

    security.declareProtected(ManagePortal, 'manage_configSin')
    def manage_configSin(self, submit, config='', filename='', \
                         REQUEST=None, **kwargs):
        """config this puppy"""
        if submit == "Set Config":
            self.parse(config)
        elif submit == "Import":
            self.load(filename)
        elif submit == "Export":
            self.save(filename)

        if REQUEST:
            return REQUEST.RESPONSE.redirect(self.absolute_url() + \
                                             "/manage_workspace")

    security.declareProtected(ManagePortal, 'manage_debug')
    def manage_debug(self, submit, maps=(),  REQUEST=None, *args, **kwargs):
        """update maps for testing"""
        if submit == "Purge":
            self._reset(self.config)
            self.parse(self.config)

        elif submit == "Update Maps":
            for id in maps:
                self.sin(id, force=1)

        if REQUEST:
            return REQUEST.RESPONSE.redirect(self.absolute_url() + \
                                             "/manage_workspace")

    security.declarePrivate('setCurrentFeed')
    def setCurrentFeed(self, name):
        self._v_current_feed = name

    security.declareProtected(View, 'getCurrentFeed')
    def getCurrentFeed(self):
        """ Get the current configured feed """
        fd = self._v_current_feed
        if fd not in self.maps.keys():
            raise ValueError, "'%s' is not a valid map" % fd
        return fd

    security.declareProtected(View, 'getChannelUri')
    def getChannelUri(self, map_name):
        """ return the uri of a given channel """
        map = self.sin_tool.maps.get(map_name)
        return map.Channels()[0]['channel'].uri    
        
    # slightly improved hack-o-rama to allow
    # using a simpler path expression: here/sintool/macros/slashdot
    security.declareProtected(View, 'macros')
    def macros(self):
        """ Allow traversing to map macro via ZPT """
        return SinMacro(self)

    macros = ComputedAttribute(macros, 1)
Ejemplo n.º 13
0
class ElementItem:
    """ ElementItem is the "term", it contains information imported from SKOS/rdf, it also contains all results from 
        google search saved as GoogleItems.

            >>> from Products.ALiSS.managers.element_item import ElementItem
            >>> eItem = ElementItem( id=            'eItem',
            ...                      name=          'eName',
            ...                      definition=    'eDefinition',
            ...                      translations=  {'en':'eEN'},
            ...                      center_parent= 'cParent',
            ...                      url=           'eURL')

        Let's check if our initiation of ElementItem worked.

            >>> eItem.id
            'eItem'
            >>> eItem.name
            'eName'
            >>> eItem.definition
            'eDefinition'
            >>> eItem.translations
            {'en': 'eEN'}
            >>> eItem.center_parent
            'cParent'
            >>> eItem.url
            'eURL'
            >>> eItem.google_disabled
            []
            >>> len(eItem.google_collection)
            0

        We have a clean ElementItem with no indexed content groups associated (GoogleItem) stored.
        Lets add some content groups.

            >>> eItem.add_google_item(id=               'itemID',
            ...                       elem_parent=      'eItem',
            ...                       center_parent=    'cParent',
            ...                       group_id=         'grpId',
            ...                       match_level=      'exact')

        We have added a google item with id=gItem, let's try to retrieve it.

            >>> from Products.ALiSS.managers.google_item import GoogleItem
            >>> ourItem = eItem.get_google_collection_item('grpId')
            >>> isinstance(ourItem, GoogleItem)
            True

        Worked fine, there is not much more we can do here except delete the google item :)

            >>> eItem.delete_google_item('grpId')
            >>> ourItem = eItem.get_google_collection_item('grpId')
            >>> ourItem == None
            True

    """
    security = ClassSecurityInfo()

    meta_type = METATYPE_ALISSELEMENT
    def __init__(self, id, name, definition, translations, center_parent, url):
        #identifiers
        self.id =                   id
        self.center_parent =        center_parent

        #element data
        self.url =                  url
        self.name =                 name
        self.definition =           definition
        self.translations =         translations

        #google data
        self.google_collection =    OOBTree()
        self.google_disabled =      []


    def __getattr__(self, name):
        """ """
        if name.startswith('objecttrans_'):
            parts = name.split('_')
            func, lang = parts[0], parts[1]
            return self.objecttrans(lang)
        if name.startswith('objectname_'):
            parts = name.split('_')
            func, lang = parts[0], parts[1]
            return self.objectname(lang)
        raise AttributeError, name

    #################################
    #   GOOGLE_COLLECTION BASICS    #
    #################################
    def __add_google_page(self, group_id, id, google_parent, elem_parent, center_parent, g_server,
                          page_title, page_url, page_snippet, page_directoryTitle, page_cachedSize, page_hostName):
        #add a page
        google_item = self.get_google_collection_item(group_id)
        if google_item:
            google_item.add_google_page(id,
                                        google_parent,
                                        elem_parent,
                                        center_parent,
                                        g_server,
                                        page_title,
                                        page_url,
                                        page_snippet,
                                        page_directoryTitle,
                                        page_cachedSize,
                                        page_hostName)


    #################
    #   BASIC API   #
    #################
    def add_google_page(self, group_id, id, google_parent, elem_parent, center_parent, g_server,
                        page_title, page_url, page_snippet, page_directoryTitle, page_cachedSize, page_hostName):
        #create a new page
        self.__add_google_page(group_id, id, google_parent, elem_parent, center_parent, g_server,
                               page_title, page_url, page_snippet, page_directoryTitle, page_cachedSize, page_hostName)

    def add_google_item(self, id, elem_parent, center_parent, group_id, match_level):
        #create a new item
        item = GoogleItem(id, elem_parent, center_parent, group_id, match_level)
        self.google_collection[group_id] = item

    def delete_google_item(self, group_id):
        #delete an item
        if self.google_collection.get(group_id):
            del self.google_collection[group_id]


    ###############
    #   GETTERS   #
    ###############
    def get_google_collection(self):
        """ get the collection """
        return self.google_collection

    def get_google_ids(self):
        """ get the google ids """
        return self.google_collection.keys()

    def get_google_collection_item(self, group_id):
        """ get an item """
        return self.google_collection.get(group_id, None)

    def get_google_objs(self):
        """ get a list with all items """
        return self.google_collection.values()

    def getCollectionStatistics(self):
        """ """
        return self.get_google_ids()

    def getDefinition(self):
        """ return the definition in UTF8 for this item.
        Use this method when you want to display on UTF8/XHTML webpages. """
        return content_filter.safeXHTMLUnicode(self.definition)

    def getUrl(self):
        """ return the escaped URL for this item.
        Use this method when you want to display valid excaped URL in <a>-tag. """
        return utUrlEncode(self.url)

#TODO: DEPRECATED
#    def getName(self):
#        """ return lower name (for catalog use) """
#        return self.name.lower()


    #####################
    #   TRANSLATIONS    #
    #####################
    def hasTranslations(self):
        """ test if the element contains any translation """
        return len(self.translations.keys())

    def getTranslations(self):
        """ return all translations """
        return self.translations

    def getTranslation(self, lang):
        """ return a translation for a given language code """
        if lang == 'en': return self.name
        try:    return self.translations[lang]
        except: return ''

    def getTranslationsList(self):
        """ """
        trans = self.translations.values()
        trans.append(self.name)
        return trans

    def getTranslationsSuggest(self):
        """ """
        return ' '.join(self.getTranslationsList())

    def objecttrans(self, lang):
        """ used to catalog translations """
        return self.getTranslation(lang)

    def objectname(self, lang):
        """ used to catalog translations """
        return self.getTranslation(lang).lower()

    ####################################
    #   GOOGLE ENABLE/DISABLE RELATED  #
    ####################################
    def mark_google_error(self, group_id):
        #mark the object if an error occur on Google querying
        my_group = self.get_google_collection_item(group_id)
        my_group.google_error = 1
        self.google_collection[group_id] = my_group

    def set_google_enable(self, state, group_id):
        #set enable/disable state
        my_group = self.get_google_collection_item(group_id)
        my_group.google_enable = state
        self.google_collection[group_id] = my_group

    def __add_google_disabled(self, content_id):
        #add a disabled content group
        if content_id not in self.google_disabled:  
            self.google_disabled.append(content_id)

    def addGoogleDisabled(self, content_ids):
        #add a disabled content group
        if not utIsListType(content_ids): 
            content_ids = [ content_ids ]

        for content_id in content_ids:
          self.__add_google_disabled(content_id)

    def __del_google_disabled(self, content_id):
        #remove a disabled content group
        if content_id in self.google_disabled:  
            self.google_disabled.remove(content_id)

    def delGoogleDisabled(self, content_ids):
        #remove a disabled content group
        if not utIsListType(content_ids): 
            content_ids = [ content_ids ]

        for content_id in content_ids:
            self.__del_google_disabled(content_id)

    def isGroupDisabled(self, group_id):
        return group_id in self.google_disabled

    security.setDefaultAccess("allow")
Ejemplo n.º 14
0
class ZODBRoleManager( BasePlugin ):

    """ PAS plugin for managing roles in the ZODB.
    """
    meta_type = 'ZODB Role Manager'

    security = ClassSecurityInfo()

    def __init__(self, id, title=None):

        self._id = self.id = id
        self.title = title

        self._roles = OOBTree()
        self._principal_roles = OOBTree()

    def manage_afterAdd( self, item, container ):

        self.addRole( 'Manager' )

        if item is self:
            role_holder = aq_parent( aq_inner( container ) )
            for role in getattr( role_holder, '__ac_roles__', () ):
                try:
                    if role not in ('Anonymous', 'Authenticated'):
                        self.addRole( role )
                except KeyError:
                    pass

    #
    #   IRolesPlugin implementation
    #
    security.declarePrivate( 'getRolesForPrincipal' )
    def getRolesForPrincipal( self, principal, request=None ):

        """ See IRolesPlugin.
        """
        result = list( self._principal_roles.get( principal.getId(), () ) )

        getGroups = getattr( principal, 'getGroups', lambda x: () )
        for group_id in getGroups():
            result.extend( self._principal_roles.get( group_id, () ) )

        return tuple( result )

    #
    #   IRoleEnumerationPlugin implementation
    #
    def enumerateRoles( self
                      , id=None
                      , exact_match=False
                      , sort_by=None
                      , max_results=None
                      , **kw
                      ):

        """ See IRoleEnumerationPlugin.
        """
        role_info = []
        role_ids = []
        plugin_id = self.getId()

        if isinstance( id, str ):
            id = [ id ]

        if exact_match and ( id ):
            role_ids.extend( id )

        if role_ids:
            role_filter = None

        else:   # Searching
            role_ids = self.listRoleIds()
            role_filter = _ZODBRoleFilter( id, **kw )

        for role_id in role_ids:

            if self._roles.get( role_id ):
                e_url = '%s/manage_roles' % self.getId()
                p_qs = 'role_id=%s' % role_id
                m_qs = 'role_id=%s&assign=1' % role_id

                info = {}
                info.update( self._roles[ role_id ] )

                info[ 'pluginid' ] = plugin_id
                info[ 'properties_url'  ] = '%s?%s' % (e_url, p_qs)
                info[ 'members_url'  ] = '%s?%s' % (e_url, m_qs)

                if not role_filter or role_filter( info ):
                    role_info.append( info )

        return tuple( role_info )

    #
    #   IRoleAssignerPlugin implementation
    #
    security.declarePrivate( 'doAssignRoleToPrincipal' )
    def doAssignRoleToPrincipal( self, principal_id, role ):
        return self.assignRoleToPrincipal( role, principal_id )

    #
    #   Role management API
    #
    security.declareProtected( ManageUsers, 'listRoleIds' )
    def listRoleIds( self ):

        """ Return a list of the role IDs managed by this object.
        """
        return self._roles.keys()

    security.declareProtected( ManageUsers, 'listRoleInfo' )
    def listRoleInfo( self ):

        """ Return a list of the role mappings.
        """
        return self._roles.values()

    security.declareProtected( ManageUsers, 'getRoleInfo' )
    def getRoleInfo( self, role_id ):

        """ Return a role mapping.
        """
        return self._roles[ role_id ]

    security.declareProtected( ManageUsers, 'addRole' )
    def addRole( self, role_id, title='', description='' ):

        """ Add 'role_id' to the list of roles managed by this object.

        o Raise KeyError on duplicate.
        """
        if self._roles.get( role_id ) is not None:
            raise KeyError, 'Duplicate role: %s' % role_id

        self._roles[ role_id ] = { 'id' : role_id
                                 , 'title' : title
                                 , 'description' : description
                                 }

    security.declareProtected( ManageUsers, 'updateRole' )
    def updateRole( self, role_id, title, description ):

        """ Update title and description for the role.

        o Raise KeyError if not found.
        """
        self._roles[ role_id ].update( { 'title' : title
                                       , 'description' : description
                                       } )

    security.declareProtected( ManageUsers, 'removeRole' )
    def removeRole( self, role_id ):

        """ Remove 'role_id' from the list of roles managed by this object.

        o Raise KeyError if not found.
        """
        for principal_id in self._principal_roles.keys():
            self.removeRoleFromPrincipal( role_id, principal_id )

        del self._roles[ role_id ]

    #
    #   Role assignment API
    #
    security.declareProtected( ManageUsers, 'listAvailablePrincipals' )
    def listAvailablePrincipals( self, role_id, search_id ):

        """ Return a list of principal IDs to whom a role can be assigned.

        o If supplied, 'search_id' constrains the principal IDs;  if not,
          return empty list.

        o Omit principals with existing assignments.
        """
        result = []

        if search_id:  # don't bother searching if no criteria

            parent = aq_parent( self )

            for info in parent.searchPrincipals( max_results=20
                                               , sort_by='id'
                                               , id=search_id
                                               , exact_match=False
                                               ):
                id = info[ 'id' ]
                title = info.get( 'title', id )
                if ( role_id not in self._principal_roles.get( id, () )
                 and role_id != id ):
                    result.append( ( id, title ) )

        return result

    security.declareProtected( ManageUsers, 'listAssignedPrincipals' )
    def listAssignedPrincipals( self, role_id ):

        """ Return a list of principal IDs to whom a role is assigned.
        """
        result = []

        for k, v in self._principal_roles.items():
            if role_id in v:
                # should be at most one and only one mapping to 'k'

                parent = aq_parent( self )
                info = parent.searchPrincipals( id=k, exact_match=True )
                assert( len( info ) in ( 0, 1 ) )
                if len( info ) == 0:
                    title = '<%s: not found>' % k
                else:
                    title = info[0].get( 'title', k )
                result.append( ( k, title ) )

        return result

    security.declareProtected( ManageUsers, 'assignRoleToPrincipal' )
    def assignRoleToPrincipal( self, role_id, principal_id ):

        """ Assign a role to a principal (user or group).

        o Return a boolean indicating whether a new assignment was created.

        o Raise KeyError if 'role_id' is unknown.
        """
        role_info = self._roles[ role_id ] # raise KeyError if unknown!

        current = self._principal_roles.get( principal_id, () )
        already = role_id in current

        if not already:
            new = current + ( role_id, )
            self._principal_roles[ principal_id ] = new

        return not already

    security.declareProtected( ManageUsers, 'removeRoleFromPrincipal' )
    def removeRoleFromPrincipal( self, role_id, principal_id ):

        """ Remove a role from a principal (user or group).

        o Return a boolean indicating whether the role was already present.

        o Raise KeyError if 'role_id' is unknown.

        o Ignore requests to remove a role not already assigned to the
          principal.
        """
        role_info = self._roles[ role_id ] # raise KeyError if unknown!

        current = self._principal_roles.get( principal_id, () )
        new = tuple( [ x for x in current if x != role_id ] )
        already = current != new

        if already:
            self._principal_roles[ principal_id ] = new

        return already

    #
    #   ZMI
    #
    manage_options = ( ( { 'label': 'Roles', 
                           'action': 'manage_roles', }
                         ,
                       )
                     + BasePlugin.manage_options
                     )

    security.declareProtected( ManageUsers, 'manage_roles' )
    manage_roles = PageTemplateFile( 'www/zrRoles'
                                   , globals()
                                   , __name__='manage_roles'
                                   )

    security.declareProtected( ManageUsers, 'manage_twoLists' )
    manage_twoLists = PageTemplateFile( '../www/two_lists'
                                      , globals()
                                      , __name__='manage_twoLists'
                                      )

    security.declareProtected( ManageUsers, 'manage_addRole' )
    def manage_addRole( self
                      , role_id
                      , title
                      , description
                      , RESPONSE
                      ):
        """ Add a role via the ZMI.
        """
        self.addRole( role_id, title, description )

        message = 'Role+added'

        RESPONSE.redirect( '%s/manage_roles?manage_tabs_message=%s'
                         % ( self.absolute_url(), message )
                         )

    security.declareProtected( ManageUsers, 'manage_updateRole' )
    def manage_updateRole( self
                         , role_id
                         , title
                         , description
                         , RESPONSE
                         ):
        """ Update a role via the ZMI.
        """
        self.updateRole( role_id, title, description )

        message = 'Role+updated'

        RESPONSE.redirect( '%s/manage_roles?role_id=%s&manage_tabs_message=%s'
                         % ( self.absolute_url(), role_id, message )
                         )

    security.declareProtected( ManageUsers, 'manage_removeRoles' )
    def manage_removeRoles( self
                          , role_ids
                          , RESPONSE
                          ):
        """ Remove one or more roles via the ZMI.
        """
        role_ids = filter( None, role_ids )

        if not role_ids:
            message = 'no+roles+selected'

        else:

            for role_id in role_ids:
                self.removeRole( role_id )

            message = 'Roles+removed'

        RESPONSE.redirect( '%s/manage_roles?manage_tabs_message=%s'
                         % ( self.absolute_url(), message )
                         )

    security.declareProtected( ManageUsers, 'manage_assignRoleToPrincipals' )
    def manage_assignRoleToPrincipals( self
                                     , role_id
                                     , principal_ids
                                     , RESPONSE
                                     ):
        """ Assign a role to one or more principals via the ZMI.
        """
        assigned = []

        for principal_id in principal_ids:
            if self.assignRoleToPrincipal( role_id, principal_id ):
                assigned.append( principal_id )

        if not assigned:
            message = 'Role+%s+already+assigned+to+all+principals' % role_id
        else:
            message = 'Role+%s+assigned+to+%s' % ( role_id
                                                 , '+'.join( assigned )
                                                 )

        RESPONSE.redirect( ( '%s/manage_roles?role_id=%s&assign=1'
                           + '&manage_tabs_message=%s'
                           ) % ( self.absolute_url(), role_id, message )
                         )

    security.declareProtected( ManageUsers, 'manage_removeRoleFromPrincipals' )
    def manage_removeRoleFromPrincipals( self
                                       , role_id
                                       , principal_ids
                                       , RESPONSE
                                       ):
        """ Remove a role from one or more principals via the ZMI.
        """
        removed = []

        for principal_id in principal_ids:
            if self.removeRoleFromPrincipal( role_id, principal_id ):
                removed.append( principal_id )

        if not removed:
            message = 'Role+%s+alread+removed+from+all+principals' % role_id
        else:
            message = 'Role+%s+removed+from+%s' % ( role_id
                                                  , '+'.join( removed )
                                                  )

        RESPONSE.redirect( ( '%s/manage_roles?role_id=%s&assign=1'
                           + '&manage_tabs_message=%s'
                           ) % ( self.absolute_url(), role_id, message )
                         )
Ejemplo n.º 15
0
class ZODBRoleManager(BasePlugin):
    """ PAS plugin for managing roles in the ZODB.
    """
    meta_type = 'ZODB Role Manager'

    security = ClassSecurityInfo()

    def __init__(self, id, title=None):

        self._id = self.id = id
        self.title = title

        self._roles = OOBTree()
        self._principal_roles = OOBTree()

    def manage_afterAdd(self, item, container):

        if item is self:
            role_holder = aq_parent(aq_inner(container))
            for role in getattr(role_holder, '__ac_roles__', ()):
                try:
                    if role not in ('Anonymous', 'Authenticated'):
                        self.addRole(role)
                except KeyError:
                    pass

        if 'Manager' not in self._roles:
            self.addRole('Manager')

    #
    #   IRolesPlugin implementation
    #
    @security.private
    def getRolesForPrincipal(self, principal, request=None):
        """ See IRolesPlugin.
        """
        result = list(self._principal_roles.get(principal.getId(), ()))

        getGroups = getattr(principal, 'getGroups', lambda x: ())
        for group_id in getGroups():
            result.extend(self._principal_roles.get(group_id, ()))

        return tuple(result)

    #
    #   IRoleEnumerationPlugin implementation
    #
    def enumerateRoles(self,
                       id=None,
                       exact_match=False,
                       sort_by=None,
                       max_results=None,
                       **kw):
        """ See IRoleEnumerationPlugin.
        """
        role_info = []
        role_ids = []
        plugin_id = self.getId()

        if isinstance(id, str):
            id = [id]

        if exact_match and (id):
            role_ids.extend(id)

        if role_ids:
            role_filter = None

        else:  # Searching
            role_ids = self.listRoleIds()
            role_filter = _ZODBRoleFilter(id, **kw)

        for role_id in role_ids:

            if self._roles.get(role_id):
                e_url = '%s/manage_roles' % self.getId()
                p_qs = 'role_id=%s' % role_id
                m_qs = 'role_id=%s&assign=1' % role_id

                info = {}
                info.update(self._roles[role_id])

                info['pluginid'] = plugin_id
                info['properties_url'] = '%s?%s' % (e_url, p_qs)
                info['members_url'] = '%s?%s' % (e_url, m_qs)

                if not role_filter or role_filter(info):
                    role_info.append(info)

        return tuple(role_info)

    #
    #   IRoleAssignerPlugin implementation
    #
    @security.private
    def doAssignRoleToPrincipal(self, principal_id, role):
        return self.assignRoleToPrincipal(role, principal_id)

    @security.private
    def doRemoveRoleFromPrincipal(self, principal_id, role):
        return self.removeRoleFromPrincipal(role, principal_id)

    #
    #   Role management API
    #
    @security.protected(ManageUsers)
    def listRoleIds(self):
        """ Return a list of the role IDs managed by this object.
        """
        return self._roles.keys()

    @security.protected(ManageUsers)
    def listRoleInfo(self):
        """ Return a list of the role mappings.
        """
        return self._roles.values()

    @security.protected(ManageUsers)
    def getRoleInfo(self, role_id):
        """ Return a role mapping.
        """
        return self._roles[role_id]

    @security.private
    def addRole(self, role_id, title='', description=''):
        """ Add 'role_id' to the list of roles managed by this object.

        o Raise KeyError on duplicate.
        """
        if self._roles.get(role_id) is not None:
            raise KeyError('Duplicate role: %s' % role_id)

        self._roles[role_id] = {
            'id': role_id,
            'title': title,
            'description': description
        }

    @security.private
    def updateRole(self, role_id, title, description):
        """ Update title and description for the role.

        o Raise KeyError if not found.
        """
        self._roles[role_id].update({
            'title': title,
            'description': description
        })

    @security.private
    def removeRole(self, role_id, REQUEST=None):
        """ Remove 'role_id' from the list of roles managed by this object.

        o Raise KeyError if not found.

        Note that if you really want to remove a role you should first
        remove it from the roles in the root of the site (at the
        bottom of the Security tab at manage_access).
        """
        for principal_id in self._principal_roles.keys():
            self.removeRoleFromPrincipal(role_id, principal_id)

        del self._roles[role_id]

    #
    #   Role assignment API
    #
    @security.protected(ManageUsers)
    def listAvailablePrincipals(self, role_id, search_id):
        """ Return a list of principal IDs to whom a role can be assigned.

        o If supplied, 'search_id' constrains the principal IDs;  if not,
          return empty list.

        o Omit principals with existing assignments.
        """
        result = []

        if search_id:  # don't bother searching if no criteria

            parent = aq_parent(self)

            for info in parent.searchPrincipals(max_results=20,
                                                sort_by='id',
                                                id=search_id,
                                                exact_match=False):
                id = info['id']
                title = info.get('title', id)
                if role_id not in self._principal_roles.get(id, ()) and \
                        role_id != id:
                    result.append((id, title))

        return result

    @security.protected(ManageUsers)
    def listAssignedPrincipals(self, role_id):
        """ Return a list of principal IDs to whom a role is assigned.
        """
        result = []

        for k, v in self._principal_roles.items():
            if role_id in v:
                # should be at most one and only one mapping to 'k'

                parent = aq_parent(self)
                info = parent.searchPrincipals(id=k, exact_match=True)

                if len(info) > 1:
                    message = ("Multiple groups or users exist with the "
                               "name '%s'. Remove one of the duplicate groups "
                               "or users." % (k))
                    LOG.error(message)
                    raise MultiplePrincipalError(message)

                if len(info) == 0:
                    title = '<%s: not found>' % k
                else:
                    title = info[0].get('title', k)
                result.append((k, title))

        return result

    @security.private
    def assignRoleToPrincipal(self, role_id, principal_id):
        """ Assign a role to a principal (user or group).

        o Return a boolean indicating whether a new assignment was created.

        o Raise KeyError if 'role_id' is unknown.
        """
        # raise KeyError if unknown!
        role_info = self._roles[role_id]  # noqa

        current = self._principal_roles.get(principal_id, ())
        already = role_id in current

        if not already:
            new = current + (role_id, )
            self._principal_roles[principal_id] = new
            self._invalidatePrincipalCache(principal_id)

        return not already

    @security.private
    def removeRoleFromPrincipal(self, role_id, principal_id):
        """ Remove a role from a principal (user or group).

        o Return a boolean indicating whether the role was already present.

        o Raise KeyError if 'role_id' is unknown.

        o Ignore requests to remove a role not already assigned to the
          principal.
        """
        # raise KeyError if unknown!
        role_info = self._roles[role_id]  # noqa

        current = self._principal_roles.get(principal_id, ())
        new = tuple([x for x in current if x != role_id])
        already = current != new

        if already:
            self._principal_roles[principal_id] = new
            self._invalidatePrincipalCache(principal_id)

        return already

    #
    #   ZMI
    #
    manage_options = (({
        'label': 'Roles',
        'action': 'manage_roles'
    }, ) + BasePlugin.manage_options)

    security.declareProtected(ManageUsers, 'manage_roles')
    manage_roles = PageTemplateFile('www/zrRoles',
                                    globals(),
                                    __name__='manage_roles')

    security.declareProtected(ManageUsers, 'manage_twoLists')
    manage_twoLists = PageTemplateFile('../www/two_lists',
                                       globals(),
                                       __name__='manage_twoLists')

    @security.protected(ManageUsers)
    @csrf_only
    @postonly
    def manage_addRole(self,
                       role_id,
                       title,
                       description,
                       RESPONSE=None,
                       REQUEST=None):
        """ Add a role via the ZMI.
        """
        self.addRole(role_id, title, description)

        message = 'Role+added'

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_roles?manage_tabs_message=%s' %
                              (self.absolute_url(), message))

    @security.protected(ManageUsers)
    @csrf_only
    @postonly
    def manage_updateRole(self,
                          role_id,
                          title,
                          description,
                          RESPONSE=None,
                          REQUEST=None):
        """ Update a role via the ZMI.
        """
        self.updateRole(role_id, title, description)

        message = 'Role+updated'

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_roles?role_id=%s&'
                              'manage_tabs_message=%s' %
                              (self.absolute_url(), role_id, message))

    @security.protected(ManageUsers)
    @csrf_only
    @postonly
    def manage_removeRoles(self, role_ids, RESPONSE=None, REQUEST=None):
        """ Remove one or more role assignments via the ZMI.

        Note that if you really want to remove a role you should first
        remove it from the roles in the root of the site (at the
        bottom of the Security tab at manage_access).
        """
        role_ids = filter(None, role_ids)

        if not role_ids:
            message = 'no+roles+selected'

        else:

            for role_id in role_ids:
                self.removeRole(role_id)

            message = 'Role+assignments+removed'

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_roles?manage_tabs_message=%s' %
                              (self.absolute_url(), message))

    @security.protected(ManageUsers)
    @csrf_only
    @postonly
    def manage_assignRoleToPrincipals(self,
                                      role_id,
                                      principal_ids,
                                      RESPONSE,
                                      REQUEST=None):
        """ Assign a role to one or more principals via the ZMI.
        """
        assigned = []

        for principal_id in principal_ids:
            if self.assignRoleToPrincipal(role_id, principal_id):
                assigned.append(principal_id)

        if not assigned:
            message = 'Role+%s+already+assigned+to+all+principals' % role_id
        else:
            message = 'Role+%s+assigned+to+%s' % (role_id, '+'.join(assigned))

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_roles?role_id=%s&assign=1'
                              '&manage_tabs_message=%s' %
                              (self.absolute_url(), role_id, message))

    @security.protected(ManageUsers)
    @csrf_only
    @postonly
    def manage_removeRoleFromPrincipals(self,
                                        role_id,
                                        principal_ids,
                                        RESPONSE=None,
                                        REQUEST=None):
        """ Remove a role from one or more principals via the ZMI.
        """
        removed = []

        for principal_id in principal_ids:
            if self.removeRoleFromPrincipal(role_id, principal_id):
                removed.append(principal_id)

        if not removed:
            message = 'Role+%s+alread+removed+from+all+principals' % role_id
        else:
            message = 'Role+%s+removed+from+%s' % (role_id, '+'.join(removed))

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_roles?role_id=%s&assign=1'
                              '&manage_tabs_message=%s' %
                              (self.absolute_url(), role_id, message))
Ejemplo n.º 16
0
class Calendar:
    """This class represents a calendar which is a set of days which contain
        information about which conferences whithin certain categories are
        happening for each of these days and for a certain access. This class
        allows to configure the date interval and the category set to be
        considered and provides operations which allow to know about what's
        happening on each of those days.

       Attributes:
        _aw - (accessControl.AccessWrapper) Information about the access for
            which the calendar will be built.
        _sDate - (datetime) Starting date for the calendar.
        _eDate - (datetime) Ending date for the calendar.
        _categList - (List) List of categories to be considered.
        _days - (OOBTree) Index of days which build up the calendar.
    """

    def __init__( self, aw, sDate, eDate, categList=[] ):
        self._aw = aw
        self._tz = sDate.tzinfo
        self._sDate = sDate.replace(hour=0, minute=0, second=0, microsecond=0)
        self._eDate = eDate.replace(hour=23, minute=59, second=59, microsecond=0)
        self._categList = categList
        self._icons = {}
        self._days = None

    def getIcons( self ):
        try:
            return self._icons
        except:
            self._icons = {}
            return {}

    def setIcons(self, categ):
        """Retrieves the list of icons in a given category tree
        """
        if categ.getIcon() != None:
            return [categ.getId()]
        res = []
        for subcat in categ.getSubCategoryList():
            res += self.setIcons(subcat)
        return res

    def getStartDate( self ):
        return self._sDate

    def getEndDate( self ):
        return self._eDate

    def getCategoryList( self ):
        return self._categList

    def getLocator( self ):
        """Returns the generic locator for the current object. This locator
            contains the folloing entries corresponding to values for which
            the calendar is configured:
                selCateg -> List of category ids.
                sDate -> Starting date.
                eDate -> Ending date.
        """
        l = Locators.Locator()
        ids = []
        for c in self.getCategoryList():
            ids.append( c.getId() )
        l["selCateg"] = ids
        l["sDate"] = self.getStartDate().strftime("%Y-%m-%d")
        l["eDate"] = self.getStartDate().strftime("%Y-%m-%d")
        return l

    def _mapConferenceToDays(self,conf,categList):
        """Registers a given conference for the days on which it is taking place
            whithin the calendar date interval.

           Parameters:
            conf - (conference.Conference) Conference to be mapped.
            categList - (List) List of calendar categories in which the
                specified conference is found.
        """

        inc = timedelta(1)
        d = max(conf.getStartDate().astimezone(self._tz).replace(hour=0,minute=0,second=0),
                self.getStartDate().astimezone(self._tz).replace(hour=0,minute=0,second=0))
        ed = min(conf.getEndDate().astimezone(self._tz), self.getEndDate().astimezone(self._tz))
        if ed > self.getEndDate():
            ed = self.getEndDate()

        while d <= ed:
            #norm_date=d.tzinfo.normalize(d)
            #norm_date=norm_date.replace(hour=0)
            norm_date=self.getNormDate(d)
            if not self._days.has_key( norm_date ):
                self._days[norm_date] = Day( self, d )

            self._days[norm_date].addConference( conf, categList, self._tz )
            d += inc

    def _initialiseDays( self ):
        """
        """
        self._days = OOBTree()
        res = set()
        self._categIdx = {}
        self._icons={}
        catDayIdx = indexes.IndexesHolder().getIndex("categoryDate")
        for categ in self.getCategoryList():
            confs = catDayIdx.getObjectsInDays(categ.getId(), self.getStartDate(), self.getEndDate())
            for conf in confs:
                confId = conf.getId()
                if not self._categIdx.has_key(confId):
                    self._categIdx[confId]=[]
                self._categIdx[confId].append(categ)
            res.update(confs)
        for conf in res:
            #getting icon from the nearest owner category
            owner = conf.getOwner()
            while owner != None and owner.getId() != "0":
                if owner.getIcon():
                    if self._icons.has_key(owner.getId()):
                        self._icons[owner.getId()].append(conf.getId())
                    else:
                        self._icons[owner.getId()] = [conf.getId()]
                    break
                owner = owner.getOwner()
            #mapping conf to days
            self._mapConferenceToDays(conf ,self._categIdx[conf.getId()])



    def getNormDate(self,date):
        # we have to normalize, but as we are going over all the days, we have to keep the date
        # and just normalize the tzinfo.
        norm_date=date.tzinfo.normalize(date)
        norm_date=norm_date.replace(year=date.year, month=date.month,day=date.day, hour=0)
        return norm_date

    def getDay( self, date ):
        if not self._days:
            self._initialiseDays()
        norm_date=self.getNormDate(date)
        if not self._days.has_key( norm_date ):
            self._days[norm_date] = Day( self, date )
        return self._days[norm_date]

    def getDayList( self ):
        inc = timedelta( 1 )
        d = self.getStartDate()
        l = []
        while d<self.getEndDate():
            l.append( self.getDay( d ) )
            d += inc
        return l

    def getConferenceCategories( self, conf ):
        return self._categIdx[conf.getId()]

    def __str__(self):
        l = []
        if self._days:
            for day in self._days.values():
                l.append("%s"%day)
        str =  _("Calendar between %s and %s: %s")%(\
                            self.getStartDate().strftime("%Y-%m-%d"), \
                            self.getEndDate().strftime("%Y-%m-%d"), \
                            "\n\t\t".join(l) )
        return str
Ejemplo n.º 17
0
class TopicIndex(Persistent, SimpleItem):

    """A TopicIndex maintains a set of FilteredSet objects.

    Every FilteredSet object consists of an expression and and IISet with all
    Ids of indexed objects that eval with this expression to 1.
    """
    implements(ITopicIndex, IPluggableIndex)

    meta_type="TopicIndex"
    query_options = ('query', 'operator')

    manage_options= (
        {'label': 'FilteredSets', 'action': 'manage_main'},
    )

    def __init__(self,id,caller=None):
        self.id = id
        self.filteredSets  = OOBTree()
        self.operators = ('or','and')
        self.defaultOperator = 'or'

    def getId(self):
        return self.id

    def clear(self):
        for fs in self.filteredSets.values():
            fs.clear()

    def index_object(self, docid, obj ,threshold=100):
        """ hook for (Z)Catalog """
        for fid, filteredSet in self.filteredSets.items():
            filteredSet.index_object(docid,obj)
        return 1

    def unindex_object(self,docid):
        """ hook for (Z)Catalog """

        for fs in self.filteredSets.values():
            try:
                fs.unindex_object(docid)
            except KeyError:
                LOG.debug('Attempt to unindex document'
                          ' with id %s failed' % docid)
        return 1

    def numObjects(self):
        """Return the number of indexed objects."""
        return "n/a"

    def indexSize(self):
        """Return the size of the index in terms of distinct values."""
        return "n/a"

    def search(self,filter_id):
        if self.filteredSets.has_key(filter_id):
            return self.filteredSets[filter_id].getIds()

    def _apply_index(self, request):
        """ hook for (Z)Catalog
            'request' --  mapping type (usually {"topic": "..." }
        """
        record = parseIndexRequest(request, self.id, self.query_options)
        if record.keys is None:
            return None

        operator = record.get('operator', self.defaultOperator).lower()
        if operator == 'or':  set_func = union
        else: set_func = intersection

        res = None
        for filter_id in record.keys:
            rows = self.search(filter_id)
            res = set_func(res,rows)

        if res:
            return res, (self.id,)
        else:
            return IITreeSet(), (self.id,)

    def uniqueValues(self,name=None, withLength=0):
        """ needed to be consistent with the interface """
        return self.filteredSets.keys()

    def getEntryForObject(self,docid, default=_marker):
        """ Takes a document ID and returns all the information we have
            on that specific object.
        """
        return self.filteredSets.keys()

    def addFilteredSet(self, filter_id, typeFilteredSet, expr):
        # Add a FilteredSet object.
        if self.filteredSets.has_key(filter_id):
            raise KeyError,\
                'A FilteredSet with this name already exists: %s' % filter_id
        self.filteredSets[filter_id] = factory(filter_id,
                                               typeFilteredSet,
                                               expr,
                                              )

    def delFilteredSet(self, filter_id):
        # Delete the FilteredSet object specified by 'filter_id'.
        if not self.filteredSets.has_key(filter_id):
            raise KeyError,\
                'no such FilteredSet:  %s' % filter_id
        del self.filteredSets[filter_id]

    def clearFilteredSet(self, filter_id):
        # Clear the FilteredSet object specified by 'filter_id'.
        if not self.filteredSets.has_key(filter_id):
            raise KeyError,\
                'no such FilteredSet:  %s' % filter_id
        self.filteredSets[filter_id].clear()

    def manage_addFilteredSet(self, filter_id, typeFilteredSet, expr, URL1, \
            REQUEST=None,RESPONSE=None):
        """ add a new filtered set """

        if len(filter_id) == 0: raise RuntimeError,'Length of ID too short'
        if len(expr) == 0: raise RuntimeError,'Length of expression too short'

        self.addFilteredSet(filter_id, typeFilteredSet, expr)

        if RESPONSE:
            RESPONSE.redirect(URL1+'/manage_workspace?'
            'manage_tabs_message=FilteredSet%20added')

    def manage_delFilteredSet(self, filter_ids=[], URL1=None, \
            REQUEST=None,RESPONSE=None):
        """ delete a list of FilteredSets"""

        for filter_id in filter_ids:
            self.delFilteredSet(filter_id)

        if RESPONSE:
            RESPONSE.redirect(URL1+'/manage_workspace?'
            'manage_tabs_message=FilteredSet(s)%20deleted')

    def manage_saveFilteredSet(self,filter_id, expr, URL1=None,\
            REQUEST=None,RESPONSE=None):
        """ save expression for a FilteredSet """

        self.filteredSets[filter_id].setExpression(expr)

        if RESPONSE:
            RESPONSE.redirect(URL1+'/manage_workspace?'
            'manage_tabs_message=FilteredSet(s)%20updated')

    def getIndexSourceNames(self):
        """ return names of indexed attributes """
        return ('n/a',)

    def getIndexQueryNames(self):
        return (self.id,)

    def manage_clearFilteredSet(self, filter_ids=[], URL1=None, \
            REQUEST=None,RESPONSE=None):
        """  clear a list of FilteredSets"""

        for filter_id in filter_ids:
            self.clearFilteredSet(filter_id)

        if RESPONSE:
            RESPONSE.redirect(URL1+'/manage_workspace?'
             'manage_tabs_message=FilteredSet(s)%20cleared')

    manage = manage_main = DTMLFile('dtml/manageTopicIndex',globals())
    manage_main._setName('manage_main')
    editFilteredSet = DTMLFile('dtml/editFilteredSet',globals())
Ejemplo n.º 18
0
class Folder(Persistent, Contained):
    """The standard Zope Folder implementation."""

    implements(IContentContainer)

    def __init__(self):
        self.data = OOBTree()

    def keys(self):
        """Return a sequence-like object containing the names
           associated with the objects that appear in the folder
        """
        return self.data.keys()

    def __iter__(self):
        return iter(self.data.keys())

    def values(self):
        """Return a sequence-like object containing the objects that
           appear in the folder.
        """
        return self.data.values()

    def items(self):
        """Return a sequence-like object containing tuples of the form
           (name, object) for the objects that appear in the folder.
        """
        return self.data.items()

    def __getitem__(self, name):
        """Return the named object, or raise ``KeyError`` if the object
           is not found.
        """
        return self.data[name]

    def get(self, name, default=None):
        """Return the named object, or the value of the `default`
           argument if the object is not found.
        """
        return self.data.get(name, default)

    def __contains__(self, name):
        """Return true if the named object appears in the folder."""
        return self.data.has_key(name)

    def __len__(self):
        """Return the number of objects in the folder."""
        return len(self.data)

    def __setitem__(self, name, object):
        """Add the given object to the folder under the given name."""

        if not (isinstance(name, str) or isinstance(name, unicode)):
            raise TypeError("Name must be a string rather than a %s" %
                            name.__class__.__name__)
        try:
            unicode(name)
        except UnicodeError:
            raise TypeError("Non-unicode names must be 7-bit-ascii only")
        if not name:
            raise TypeError("Name must not be empty")

        if name in self.data:
            raise KeyError("name, %s, is already in use" % name)

        setitem(self, self.data.__setitem__, name, object)

    def __delitem__(self, name):
        """Delete the named object from the folder. Raises a KeyError
           if the object is not found."""
        uncontained(self.data[name], self, name)
        del self.data[name]
Ejemplo n.º 19
0
import pandas
from BTrees.OOBTree import OOBTree
import pickle
t = OOBTree()

class content:
    def __init__(self):
        self.row_num = []
        self.business_id = []
    def insert(self,row,b_id):
        self.row_num.append(row)
        self.business_id.append(b_id)
        
file2 = open(r'../test/funny.pkl', 'rb')
t = pickle.load(file2)
file2.close()
#print(list(t.values(8)))
for item in list(t.values(8,8)):
    print(item.row_num,item.business_id)
Ejemplo n.º 20
0
class PathIndex(Persistent, Implicit, SimpleItem):
    """ A path index stores all path components of the physical
    path of an object:

    Internal datastructure:

    - a physical path of an object is split into its components

    - every component is kept as a  key of a OOBTree in self._indexes

    - the value is a mapping 'level of the path component' to
      'all documentIds with this path component on this level'

    """

    __implements__ = (PluggableIndex.PluggableIndexInterface,)

    meta_type="PathIndex"

    manage_options= (
        {'label': 'Settings',
         'action': 'manage_main',
         'help': ('PathIndex','PathIndex_Settings.stx')},
    )

    query_options = ["query","level","operator"]


    def __init__(self,id,caller=None):
        self.id = id

        # experimental code for specifing the operator
        self.operators = ['or','and']
        self.useOperator = 'or'

        self.clear()


    def getId(self): return self.id

    def clear(self):
        """ clear everything """

        self._depth   = 0
        self._index   = OOBTree()
        self._unindex = IOBTree()


    def insertEntry(self,comp,id,level):
        """
        k is a path component (generated by splitPath() )
        v is the documentId
        level is the level of the component inside the path
        """

        if self._index.has_key(comp)==0:
            self._index[comp] = IOBTree()

        if self._index[comp].has_key(level)==0:
            self._index[comp][level] = IISet()

        self._index[comp][level].insert(id)

        if level > self._depth: self._depth = level



    def index_object(self, documentId, obj ,threshold=100):
        """ hook for (Z)Catalog """

        # first we check if the object provide an attribute or
        # method to be used as hook for the PathIndex

        if hasattr(obj,self.id):
            f = getattr(obj,self.id)

            try:
                if callable(f): path = f()
                else:           path = f
            except:
                return 0

            if not (isinstance(path,StringType) or
                    isinstance(path,TupleType)):
                raise TypeError, "attribute/method must be/return string or tuple"

        else:

            try:
                path = obj.getPhysicalPath()
            except:
                return 0

        if type(path) in (ListType,TupleType):
            path = '/'+ '/'.join(path[1:])

        comps = self.splitPath(path,obj)

#        if obj.meta_type != 'Folder':
#            comps = comps[:-1]

        for i in range(len(comps)):
            self.insertEntry( comps[i],documentId,i)

        self._unindex[documentId] = path

        return 1


    def unindex_object(self,documentId):
        """ hook for (Z)Catalog """

        if not self._unindex.has_key(documentId):
            LOG(self.__class__.__name__, ERROR,
                'Attempt to unindex nonexistent document'
                ' with id %s' % documentId)
            return
        
        path = self._unindex[documentId]
        comps = path.split('/')

        for level in range(len(comps[1:])):
            comp = comps[level+1]

            try:
                self._index[comp][level].remove(documentId)

                if len(self._index[comp][level])==0:
                    del self._index[comp][level]

                if len(self._index[comp])==0:
                    del self._index[comp]
            except KeyError:
                LOG(self.__class__.__name__, ERROR,
                    'Attempt to unindex document'
                    ' with id %s failed' % documentId)


        del self._unindex[documentId]


    def printIndex(self):
        for k,v in self._index.items():
            print "-"*78
            print k
            for k1,v1 in v.items():
                print k1,v1,

            print


    def splitPath(self,path,obj=None):
        """ split physical path of object. If the object has
        as function splitPath() we use this user-defined function
        to split the path
        """

        if hasattr(obj,"splitPath"):
            comps = obj.splitPath(path)
        else:
            comps = filter(lambda x: x , re.split("/",path))

        return comps


    def search(self,path,default_level=0):
        """
        path is either a string representing a
        relative URL or a part of a relative URL or
        a tuple (path,level).

        level>=0  starts searching at the given level
        level<0   not implemented yet
        """

        if isinstance(path,StringType):
            level = default_level
        else:
            level = int(path[1])
            path  = path[0]

        comps = self.splitPath(path)

        if len(comps) == 0:
            return IISet(self._unindex.keys())

        if level >=0:

            results = []
            for i in range(len(comps)):
                comp = comps[i]

                if not self._index.has_key(comp): return IISet()
                if not self._index[comp].has_key(level+i): return IISet()

                results.append( self._index[comp][level+i] )
            
            res = results[0]

            for i in range(1,len(results)):
                res = intersection(res,results[i])

            return res

        else:

            results = IISet()

            for level in range(0,self._depth + 1):

                ids = None
                error = 0

                for cn in range(0,len(comps)):
                    comp = comps[cn]

                    try:
                        ids = intersection(ids,self._index[comp][level+cn])
                    except KeyError:
                        error = 1

                if error==0:
                    results = union(results,ids)

            return results



    def __len__(self):
        """ len """
        return len(self._index)


    def numObjects(self):
        """ return the number of indexed objects"""
        return len(self._unindex)


    def keys(self):
        """ return list of all path components """
        keys = []
        for k in self._index.keys(): keys.append(k)
        return keys


    def values(self):
        values = []
        for k in self._index.values(): values.append(k)
        return values


    def items(self):
        """ mapping path components : documentIds """

        items = []
        for k in self._index.items(): items.append(k)
        return items


    def _apply_index(self, request, cid=''):
        """ hook for (Z)Catalog
        request   mapping type (usually {"path": "..." }
                  additionaly a parameter "path_level" might be passed
                  to specify the level (see search())

        cid      ???
        """

        record = parseIndexRequest(request,self.id,self.query_options)
        if record.keys==None: return None

        if request.has_key('%s_level' % cid):
            warnings.warn("The usage of the '%s_level' "
                          "is no longer recommended.\n"
                          "Please use a mapping object and the "
                          "'level' key to specify the operator." % cid)


        # get the level parameter
        level    = record.get("level",0)

        # experimental code for specifing the operator
        operator = record.get('operator',self.useOperator).lower()

        # depending on the operator we use intersection of union
        if operator=="or":  set_func = union
        else:               set_func = intersection

        res = None

        for k in record.keys:
            rows = self.search(k,level)
            res = set_func(res,rows)

        if res:
            return res, (self.id,)
        else:
            return IISet(), (self.id,)


    def uniqueValues(self,name=None,withLength=0):
        """ needed to be consistent with the interface """

        return self._index.keys()


    def getEntryForObject(self,documentId,default=_marker):
        """ Takes a document ID and returns all the information we have
        on that specific object. """

        try:
            return self._unindex[documentId]
        except:
            return None


    index_html = DTMLFile('dtml/index', globals())
    manage_workspace = DTMLFile('dtml/managePathIndex', globals())
Ejemplo n.º 21
0
class MemberDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase):
    '''This tool wraps user objects, making them act as Member objects.
    '''
    id = 'portal_memberdata'
    meta_type = 'CMF Member Data Tool'
    _actions = []
    _v_temps = None
    _properties = ()

    security = ClassSecurityInfo()

    manage_options=( ActionProviderBase.manage_options +
                     ({ 'label' : 'Overview'
                       , 'action' : 'manage_overview'
                       }
                     , { 'label' : 'Contents'
                       , 'action' : 'manage_showContents'
                       }
                     )
                   + PropertyManager.manage_options
                   + SimpleItem.manage_options
                   )

    #
    #   ZMI methods
    #
    security.declareProtected( CMFCorePermissions.ManagePortal
                             , 'manage_overview' )
    manage_overview = DTMLFile( 'explainMemberDataTool', _dtmldir )

    security.declareProtected( CMFCorePermissions.ViewManagementScreens
                             , 'manage_showContents')
    manage_showContents = DTMLFile('memberdataContents', _dtmldir )

    security.declareProtected( CMFCorePermissions.ViewManagementScreens
                             , 'getContentsInformation',)


    def __init__(self):
        self._members = OOBTree()
        # Create the default properties.
        self._setProperty('email', '', 'string')
        self._setProperty('portal_skin', '', 'string')
        self._setProperty('listed', '', 'boolean')
        self._setProperty('login_time', '2000/01/01', 'date')
        self._setProperty('last_login_time', '2000/01/01', 'date')

    #
    #   'portal_memberdata' interface methods
    #
    security.declarePrivate('listActions')
    def listActions(self, info=None):
        """
        Return actions provided via tool.
        """
        return self._actions

    security.declarePrivate('getMemberDataContents')
    def getMemberDataContents(self):
        '''
        Return the number of members stored in the _members
        BTree and some other useful info
        '''
        membertool   = getToolByName(self, 'portal_membership')
        members      = self._members
        user_list    = membertool.listMemberIds()
        member_list  = members.keys()
        member_count = len(members)
        orphan_count = 0

        for member in member_list:
            if member not in user_list:
                orphan_count = orphan_count + 1

        return [{ 'member_count' : member_count,
                  'orphan_count' : orphan_count }]

    security.declarePrivate( 'searchMemberDataContents' )
    def searchMemberDataContents( self, search_param, search_term ):
        """ Search members """
        res = []

        if search_param == 'username':
            search_param = 'id'

        for user_wrapper in self._members.values():
            searched = getattr( user_wrapper, search_param, None )
            if searched is not None and string.find( searched, search_term ) != -1:
                res.append( { 'username' : getattr( user_wrapper, 'id' )
                            , 'email' : getattr( user_wrapper, 'email', '' )
                            }
                          )

        return res

    security.declarePrivate('pruneMemberDataContents')
    def pruneMemberDataContents(self):
        '''
        Compare the user IDs stored in the member data
        tool with the list in the actual underlying acl_users
        and delete anything not in acl_users
        '''
        membertool= getToolByName(self, 'portal_membership')
        members   = self._members
        user_list = membertool.listMemberIds()

        for tuple in members.items():
            member_name = tuple[0]
            member_obj  = tuple[1]
            if member_name not in user_list:
                del members[member_name]

    security.declarePrivate('wrapUser')
    def wrapUser(self, u):
        '''
        If possible, returns the Member object that corresponds
        to the given User object.
        '''
        id = u.getUserName()
        members = self._members
        if not members.has_key(id):
            # Get a temporary member that might be
            # registered later via registerMemberData().
            temps = self._v_temps
            if temps is not None and temps.has_key(id):
                m = temps[id]
            else:
                base = aq_base(self)
                m = MemberData(base, id)
                if temps is None:
                    self._v_temps = {id:m}
                else:
                    temps[id] = m
        else:
            m = members[id]
        # Return a wrapper with self as containment and
        # the user as context.
        return m.__of__(self).__of__(u)

    security.declarePrivate('registerMemberData')
    def registerMemberData(self, m, id):
        '''
        Adds the given member data to the _members dict.
        This is done as late as possible to avoid side effect
        transactions and to reduce the necessary number of
        entries.
        '''
        self._members[id] = m
Ejemplo n.º 22
0
class FieldIndex(persistent.Persistent):

    zope.interface.implements(
        interfaces.IInjection,
        interfaces.IStatistics,
        interfaces.IIndexSearch,
        )

    def __init__(self):
        self.clear()

    def clear(self):
        """Initialize forward and reverse mappings."""
        # The forward index maps indexed values to a sequence of docids
        self._fwd_index = OOBTree()
        # The reverse index maps a docid to its index value
        self._rev_index = IOBTree()
        self._num_docs = Length(0)

    def documentCount(self):
        """See interface IStatistics"""
        return self._num_docs()

    def wordCount(self):
        """See interface IStatistics"""
        return len(self._fwd_index)

    def index_doc(self, docid, value):
        """See interface IInjection"""
        rev_index = self._rev_index
        if docid in rev_index:
            # unindex doc if present
            self.unindex_doc(docid)

        # Insert into forward index.
        set = self._fwd_index.get(value)
        if set is None:
            set = IFTreeSet()
            self._fwd_index[value] = set
        set.insert(docid)

        # increment doc count
        self._num_docs.change(1)

        # Insert into reverse index.
        rev_index[docid] = value

    def unindex_doc(self, docid):
        """See interface IInjection"""
        rev_index = self._rev_index
        value = rev_index.get(docid)
        if value is None:
            return # not in index

        del rev_index[docid]

        try:
            set = self._fwd_index[value]
            set.remove(docid)
        except KeyError:
            # This is fishy, but we don't want to raise an error.
            # We should probably log something.
            # but keep it from throwing a dirty exception
            set = 1

        if not set:
            del self._fwd_index[value]

        self._num_docs.change(-1)

    def apply(self, query):
        if len(query) != 2 or not isinstance(query, tuple):
            raise TypeError("two-length tuple expected", query)
        return multiunion(self._fwd_index.values(*query))        
Ejemplo n.º 23
0
class JobCatalogTool(CatalogTool):
    """
    A specific catalog for indexing Jobs.
    """

    title = 'Job Catalog'
    id = 'job_catalog'
    portal_type = meta_type = 'JobCatalog'
    plone_tool = 1

    security = ClassSecurityInfo()
    _properties = (
        {'id': 'title', 'type': 'string', 'mode': 'w'},
    )

    def __init__(self):
        super(JobCatalogTool, self).__init__()
        self._catalog = JobCatalog()
        self.jobs = OOBTree()

    security.declareProtected(SearchZCatalog, 'resolve_path')

    def resolve_path(self, path):
        # Attempt to resolve a job id (path) within this catalog.
        # The path is meant to be  a job id.
        # If no object is found, None is returned.
        # No exceptions are raised.
        try:
            return self.jobs[path]
        except Exception:
            pass

    security.declareProtected(ManageZCatalogEntries, 'clearFindAndRebuild')

    def clearFindAndRebuild(self):
        """
        Empties catalog, then finds all contentish objects (i.e. objects
        with an indexObject method), and reindexes them.
        This may take a long time.
        """
        self.manage_catalogClear()

        for job in self.jobs.values():
            self.reindexObject(job, uid=job.id)

    security.declareProtected(ManageZCatalogEntries, 'reindexIndex')

    def reindexIndex(self, name, REQUEST, pghandler=None):
        if isinstance(name, str):
            name = (name, )

        paths = self._catalog.uids.keys()

        i = 0
        if pghandler:
            pghandler.init('reindexing %s' % name, len(paths))

        for p in paths:
            i += 1
            if pghandler:
                pghandler.report(i)

            obj = self.resolve_path(p)
            if obj is None:
                LOG.error('reindexIndex could not resolve '
                          'an object from the uid %r.' % p)
            else:
                # don't update metadata when only reindexing a single
                # index via the UI
                self.catalog_object(obj, p, idxs=name,
                                    update_metadata=0, pghandler=pghandler)

        if pghandler:
            pghandler.finish()
Ejemplo n.º 24
0
class TagNode(persistent.Persistent):
    """ Node of TagTree.
    """

    def __init__(self, name, parent=None):
        """
        :param name:
            Name of this TaggedNode. (Not a full name.) None means
            that this node is a root of tree.
        :type name: unicode or None
        :param parent: Parent node of this.
        :type parent: TagNode
        """

        self._name = name
        self._parent = parent
        self._children = OOBTree()
        self._objects = IOBTree()

    def destroy(self):
        """ Untags all objects, tagged by this TagNode, and also destroys
        all children recursively.
        """

        for node in self._children.values():
            node.destroy()

        tag = self.get_tag()
        for obj in list(self._objects.values()):
            obj.remove_tag(tag)

    def get_parent(self):
        """ Returns parent node.
        """
        return self._parent

    def get_name(self):
        """ Returns name (tag level) of this node.
        """
        return self._name

    def create_child_node(self, name):
        """ Creates and returns child node.
        """

        if self._children.has_key(name):
            raise KeyError(
                    u'TagNode already has child with name {0}.'.format(
                        name))
        else:
            self._children[name] = TagNode(name, self)
            return self._children[name]

    def delete_child_node(self, name):
        """ Deletes child node.
        """

        if not self._children.has_key(name):
            raise KeyError(
                    u'TagNode does not have child with name {0}.'.format(
                        name))
        else:
            self._children[name].destroy()
            del self._children[name]

    def get_child_node(self, name):
        """ Returns child node.
        """

        try:
            return self._children[name]
        except KeyError:
            raise KeyError(
                    u'TagNode does not have child with name {0}.'.format(
                        name))

    def get_tag(self):
        """ Returns :py:class:`Tag`, which this node represents.
        """

        levels = []
        node = self
        while node.get_parent():
            levels.append(node.get_name())
            node = node.get_parent()
        return Tag(reversed(levels))

    def add_object(self, obj):
        """ Adds object to tagged objects list.
        """

        if self._objects.has_key(obj.get_id()):
            raise KeyError(u'Object already tagged.')
        else:
            self._objects[obj.get_id()] = obj

    def remove_object(self, obj):
        """ Removes object from tagged objects list.

        :param obj: TaggedObject id or TaggedObject itself.
        """

        if isinstance(obj, int):
            del self._objects[obj]
        else:
            del self._objects[obj.get_id()]

    def get_object(self, object_id):
        """ Returns object by its id.
        """

        return self._objects[object_id]

    def get_object_dict(self, tag, filter):
        """ Returns dict (id->object) of all children nodes objects tagged
        with tag for which filter returns True.

        If ``tag`` is None, returns all objects (nodes and its children)
        which pass filter.

        :type tag: list of unicode strings or None
        """

        if tag is None or len(tag) == 0:
            # Collect all objects.
            objects = dict([
                (obj.get_id(), obj)
                for obj in self._objects.values() if filter(obj)])
            for node in self._children.values():
                objects.update(node.get_object_dict(None, filter))
            return objects
        else:
            # Recursively call child node.
            name = tag.pop()
            return self._children[name].get_object_dict(tag, filter)

    def get_object_list(self, tag=None, filter=lambda x: True):
        """ Returns list of all objects which passes tag and filter.

        +   If ``tag`` is None, returns all objects, which passes filter.
        +   Otherwise returns objects, which belongs to child TagNode
            with full name ``tag``, and which passes filter.

        :type tag: Tag or None
        """

        if tag is None:
            return self.get_object_dict(tag, filter).values()
        else:
            return self.get_object_dict(
                    list(reversed(tag.as_tuple())), filter).values()
Ejemplo n.º 25
0
class LookupTable(base.Base):
    "LookupTable class"

    meta_type = "LookupTable"
    security = ClassSecurityInfo()
    records = None
    recordsLength = None

    drawDict = base.Base.drawDict.copy()
    drawDict['drawTable'] = 'drawTable'

    security.declareProtected('View management screens', 'edit')

    def edit(self, *args, **kw):
        "Inline edit short object"
        format = "<p>Currently there are %s records</p><div>%s</div>"
        if self.records is None:
            self.records = OOBTree()
        lenRecords = self.recordsLength(
        ) if self.recordsLength is not None else 0
        return format % (lenRecords, self.create_button('clear', "Clear"))

    security.declarePrivate('processRecorderChanges')

    def processRecorderChanges(self, form):
        "process the recorder changes"
        clear = form.pop('clear', None)
        if clear is not None:
            self.clear()

    security.declarePrivate('after_manage_edit')

    def before_manage_edit(self, form):
        "process the edits"
        self.processRecorderChanges(form)

    security.declareProtected('View management screens', "drawTable")

    def drawTable(self):
        "Render page"
        temp = []
        format = '<p>%s:%s</p>'
        if self.records is not None:
            for key, value in self.records.items():
                temp.append(format % (repr(key), repr(value)))
        return ''.join(temp)

    security.declareProtected('Python Record Modification', 'insert')

    def insert(self, key, value):
        "modify this key and value into the OOBTree"
        if self.records is None:
            self.records = OOBTree()
        if self.recordsLength is None:
            self.setObject('recordsLength', BTrees.Length.Length())

        if key not in self.records:
            self.recordsLength.change(1)
        self.records.insert(key, value)

    security.declareProtected('Python Record Modification', 'add')

    def add(self, key, value):
        "this this key and value into the OOBTree"
        if self.records is None:
            self.records = OOBTree()
        if self.recordsLength is None:
            self.setObject('recordsLength', BTrees.Length.Length())

        if key not in self.records:
            self.recordsLength.change(1)
        self.records[key] = value

    security.declareProtected('Python Record Access', 'items')

    def items(self, min=None, max=None):
        "return the items in this OOBTree"
        if self.records is None:
            return []
        return self.records.items(min, max)

    security.declareProtected('Python Record Access', 'values')

    def values(self, min=None, max=None):
        "return the values of this OOBTree"
        if self.records is None:
            return []
        return self.records.values(min, max)

    security.declareProtected('Python Record Modification', 'update')

    def update(self, collection):
        "update our OOBTree with the data in collection"
        if self.records is None:
            self.records = OOBTree()
        if self.recordsLength is None:
            self.setObject('recordsLength', BTrees.Length.Length())

        records = self.records
        change = self.recordsLength.change
        for key, value in collection.items():
            if key not in records:
                change(1)
            records[key] = value

    security.declareProtected('Python Record Access', 'keys')

    def keys(self, min=None, max=None):
        "return the keys of this OOBTree"
        if self.records is None:
            return []
        return self.records.keys(min, max)

    security.declareProtected('Python Record Modification', '__delitem__')

    def __delitem__(self, key):
        "delete this key from the OOBTree"
        if self.records is not None:
            del self.records[key]
            self.recordsLength.change(-1)

    security.declareProtected('Python Record Modification', 'remove')

    def remove(self, key):
        "delete this key from the OOBTree"
        if self.records is not None:
            del self.records[key]
            self.recordsLength.change(-1)

    security.declareProtected('Python Record Modification', '__setitem__')

    def __setitem__(self, key, value):
        "set this key and value in the OOBTree"
        if self.records is None:
            self.records = OOBTree()
        self.records[key] = value

    security.declareProtected('Python Record Access', '__getitem__')

    def __getitem__(self, index):
        "get this item from the OOBTree"
        if self.records is not None:
            return self.records[index]
        raise KeyError, index

    security.declareProtected('Python Record Access', 'get')

    def get(self, key, default=None):
        "get this item from the OOBTree"
        if self.records is not None:
            return self.records.get(key, default)
        return default

    security.declareProtected('Python Record Access', 'has_key')

    def has_key(self, key):
        "see if we have this key in the OOBTree"
        if self.records is not None:
            return self.records.has_key(key)
        return False

    security.declareProtected('Python Record Modification', 'clear')

    def clear(self):
        "clear the OOBTree"
        self.setObject('records', None)
        self.setObject('recordsLength', None)

    security.declarePrivate("PrincipiaSearchSource")

    def PrincipiaSearchSource(self):
        "This is the basic search function"
        return ''

    security.declarePrivate('classUpgrader')

    def classUpgrader(self):
        "upgrade this class"
        self.createBTreeLength()

    security.declarePrivate('createBTreeLength')

    def createBTreeLength(self):
        "remove Filters that are not being used"
        if self.records is not None:
            length = BTrees.Length.Length()
            length.set(len(self.records))
            self.setObject('recordsLength', length)

    createBTreeLength = utility.upgradeLimit(createBTreeLength, 165)
Ejemplo n.º 26
0
class LookupTable(base.Base):
    "LookupTable class"

    meta_type = "LookupTable"
    security = ClassSecurityInfo()
    records = None
    recordsLength = None

    drawDict = base.Base.drawDict.copy()
    drawDict['drawTable'] = 'drawTable'

    security.declareProtected('View management screens', 'edit')
    def edit(self, *args, **kw):
        "Inline edit short object"
        format = "<p>Currently there are %s records</p><div>%s</div>"
        if self.records is None:
            self.records = OOBTree()
        lenRecords = self.recordsLength() if self.recordsLength is not None else 0
        return format % (lenRecords, self.create_button('clear', "Clear"))

    security.declarePrivate('processRecorderChanges')
    def processRecorderChanges(self, form):
        "process the recorder changes"
        clear = form.pop('clear', None)
        if clear is not None:
            self.clear()

    security.declarePrivate('after_manage_edit')
    def before_manage_edit(self, form):
        "process the edits"
        self.processRecorderChanges(form)

    security.declareProtected('View management screens', "drawTable")
    def drawTable(self):
        "Render page"
        temp = []
        format = '<p>%s:%s</p>'
        if self.records is not None:
            for key,value in self.records.items():
                temp.append(format % (repr(key), repr(value)))
        return ''.join(temp)

    security.declareProtected('Python Record Modification', 'insert')
    def insert(self, key, value):
        "modify this key and value into the OOBTree"
        if self.records is None:
            self.records = OOBTree()
        if self.recordsLength is None:
            self.setObject('recordsLength' ,BTrees.Length.Length())
        
        if key not in self.records:
            self.recordsLength.change(1)
        self.records.insert(key,value)

    security.declareProtected('Python Record Modification', 'add')
    def add(self, key, value):
        "this this key and value into the OOBTree"
        if self.records is None:
            self.records = OOBTree()
        if self.recordsLength is None:
            self.setObject('recordsLength' ,BTrees.Length.Length())
        
        if key not in self.records:
            self.recordsLength.change(1)
        self.records[key] = value

    security.declareProtected('Python Record Access', 'items')
    def items(self, min=None, max=None):
        "return the items in this OOBTree"
        if self.records is None:
            return []
        return self.records.items(min, max)

    security.declareProtected('Python Record Access', 'values')
    def values(self, min=None, max=None):
        "return the values of this OOBTree"
        if self.records is None:
            return []
        return self.records.values(min, max)

    security.declareProtected('Python Record Modification', 'update')
    def update(self, collection):
        "update our OOBTree with the data in collection"
        if self.records is None:
            self.records = OOBTree()
        if self.recordsLength is None:
            self.setObject('recordsLength' ,BTrees.Length.Length())
        
        records = self.records
        change = self.recordsLength.change
        for key,value in collection.items():
            if key not in records:
                change(1)
            records[key] = value

    security.declareProtected('Python Record Access', 'keys')
    def keys(self, min=None, max=None):
        "return the keys of this OOBTree"
        if self.records is None:
            return []
        return self.records.keys(min,max)

    security.declareProtected('Python Record Modification', '__delitem__')
    def __delitem__(self, key):
        "delete this key from the OOBTree"
        if self.records is not None:
            del self.records[key]
            self.recordsLength.change(-1)

    security.declareProtected('Python Record Modification', 'remove')
    def remove(self, key):
        "delete this key from the OOBTree"
        if self.records is not None:
            del self.records[key]
            self.recordsLength.change(-1)

    security.declareProtected('Python Record Modification', '__setitem__')
    def __setitem__(self, key, value):
        "set this key and value in the OOBTree"
        if self.records is None:
            self.records = OOBTree()
        self.records[key] = value

    security.declareProtected('Python Record Access', '__getitem__')
    def __getitem__(self, index):
        "get this item from the OOBTree"
        if self.records is not None:
            return self.records[index]
        raise KeyError, index

    security.declareProtected('Python Record Access', 'get')
    def get(self, key, default=None):
        "get this item from the OOBTree"
        if self.records is not None:
            return self.records.get(key,default)
        return default

    security.declareProtected('Python Record Access', 'has_key')
    def has_key(self, key):
        "see if we have this key in the OOBTree"
        if self.records is not None:
            return self.records.has_key(key)
        return False

    security.declareProtected('Python Record Modification', 'clear')
    def clear(self):
        "clear the OOBTree"
        self.setObject('records', None)
        self.setObject('recordsLength', None)
        
    security.declarePrivate("PrincipiaSearchSource")
    def PrincipiaSearchSource(self):
        "This is the basic search function"
        return ''
      
    security.declarePrivate('classUpgrader')
    def classUpgrader(self):
        "upgrade this class"
        self.createBTreeLength() 
      
    security.declarePrivate('createBTreeLength')
    def createBTreeLength(self):
        "remove Filters that are not being used"
        if self.records is not None:
            length = BTrees.Length.Length()
            length.set(len(self.records))
            self.setObject('recordsLength', length)
    createBTreeLength = utility.upgradeLimit(createBTreeLength, 165)
Ejemplo n.º 27
0
class LinkSet(Persistent, Contained):
    """Set of links.

    This class is used internally to represent relationships.  Initially it
    is empty

        >>> linkset = LinkSet()
        >>> list(linkset)
        []

    You can add new links to it

        >>> from schooltool.relationship.tests import URIStub
        >>> link1 = Link('example:Group', object(), URIStub('example:Member'),
        ...              URIStub('example:Membership'))
        >>> link2 = Link('example:Friend', object(), URIStub('example:Friend'),
        ...              URIStub('example:Friendship'))
        >>> linkset.add(link1)
        >>> linkset.add(link2)

    The links have landed in the cache too:

        >>> expected = {
        ...     'example:Member': [link1],
        ...     'example:Friend': [link2]}
        >>> dict(linkset._byrole.items()) == expected
        True

    Let's zap the cache and call getCachedLinksByRole(), which should restore it:

        >>> del linkset._byrole
        >>> linkset.getCachedLinksByRole(URIStub('something'))
        []

        >>> dict(linkset._byrole.items()) == expected
        True

    Links should get named:

        >>> link1.__name__
        '1'
        >>> link2.__name__
        '2'

    We can access our links through their names:

        >>> linkset['1'] is link1
        True
        >>> linkset['2'] is link2
        True

    And get parents set:

        >>> link1.__parent__ is linkset
        True

    We got them in the container now:

        >>> set(linkset) == set([link1, link2]) # order is not preserved
        True

    You can look for links for a specific relationship

        >>> linkset.find('example:Group',
        ...              link1.target,
        ...              URIStub('example:Member'),
        ...              URIStub('example:Membership')) is link1
        True

    We can't add same link into the container twice:

        >>> linkset.add(link1)                      # doctest: +ELLIPSIS
        Traceback (most recent call last):
          ...
        ValueError: ...

    If find fails, it raises ValueError, just like list.index.

        >>> linkset.find('example:Member', link1.target,
        ...              URIStub('example:Group'),
        ...              URIStub('example:Membership'))      # doctest: +ELLIPSIS
        Traceback (most recent call last):
          ...
        ValueError: ...

    You can remove links

        >>> linkset.remove(link2)
        >>> set(linkset) == set([link1])
        True

    The links are removed from the cache too:

        >>> list(linkset._byrole.keys())
        ['example:Member']

    If you try to remove a link that is not in the set, you will get a
    ValueError.

        >>> linkset.remove(link2)                   # doctest: +ELLIPSIS
        Traceback (most recent call last):
          ...
        ValueError: ...

    You can remove all links

        >>> linkset.clear()
        >>> set(linkset) == set([])
        True

    The cache has been cleared too:

        >>> len(linkset._byrole)
        0

    The class is documented in IRelationshipLinks

        >>> from zope.interface.verify import verifyObject
        >>> verifyObject(IRelationshipLinks, linkset)
        True

    """

    implements(IRelationshipLinks)

    _lids = None

    def __init__(self):
        self._lids = IFBTree.TreeSet()
        self._links = OOBTree()

    @property
    def catalog(self):
        return getLinkCatalog()

    def getCachedLinksByRole(self, role, catalog=None):
        """Get a set of links by role."""
        if catalog is None:
            catalog = self.catalog
        lids = self.query(role=role, catalog=catalog)
        return [CLink(catalog, lid) for lid in lids]

    def getCachedLinksByTarget(self, target, catalog=None):
        if catalog is None:
            catalog = self.catalog
        lids = self.query(target=target, catalog=catalog)
        return [CLink(catalog, lid) for lid in lids]

    def add(self, link):
        if link.__parent__ == self:
            raise ValueError("You are adding same link twice.")

        i = 1
        while "%s" % i in self._links:
            i += 1
        link.__name__ = "%s" % i
        self._links[link.__name__] = link
        link.__parent__ = self
        notify(ObjectAddedEvent(link, self._links, link.__name__))

    def remove(self, link):
        if link is self._links.get(link.__name__):
            link_name = link.__name__
            self._lids.remove(getUtility(IIntIds).getId(link))
            del self._links[link.__name__]
            notify(ObjectRemovedEvent(link, self._links, link_name))
        else:
            raise ValueError("This link does not belong to this container!")

    def clear(self):
        deleted = list(self._links.items())
        self._links.clear()
        self._byrole.clear()
        for name, link in deleted:
            notify(ObjectRemovedEvent(link, self._links, name))

    def __iter__(self):
        return iter(self._links.values())

    def find(self, my_role, target, role, rel_type):
        for link in self._links.values():
            if (link.role_hash == hash(role) and
                link.target is target and
                link.rel_type_hash == hash(rel_type) and
                link.my_role_hash == hash(my_role)):
                return link
        else:
            raise ValueError(my_role, target, role, rel_type)

    def __getitem__(self, id):
        return self._links[id]

    def get(self, key, default=None):
        return self._links.get(key, default)

    def query(self, my_role=None, target=None, role=None, rel_type=None, catalog=None):
        if catalog is None:
            catalog = self.catalog
        empty = IFBTree.TreeSet()
        this_hash = hash_persistent(self.__parent__)
        result = None
        if my_role is not None:
            ids = catalog['my_role_hash'].values_to_documents.get(
                (hash(my_role), this_hash), empty)
            if result is None:
                result = ids
            else:
                result = IFBTree.intersection(result, ids)
            if not result:
                return result
        if target is not None:
            ids = catalog['target'].values_to_documents.get(
                (IKeyReference(target), this_hash), empty)
            if result is None:
                result = ids
            else:
                result = IFBTree.intersection(result, ids)
            if not result:
                return result
        if role is not None:
            ids = catalog['role_hash'].values_to_documents.get(
                (hash(role), this_hash), empty)
            if result is None:
                result = ids
            else:
                result = IFBTree.intersection(result, ids)
            if not result:
                return result
        if rel_type is not None:
            ids = catalog['rel_type_hash'].values_to_documents.get(
                (hash(rel_type), this_hash), empty)
            if result is None:
                result = ids
            else:
                result = IFBTree.intersection(result, ids)
        return result

    def iterLinksByRole(self, role, rel_type=None, catalog=None):
        if catalog is None:
            catalog = self.catalog
        lids = self.query(role=role, rel_type=rel_type, catalog=catalog)
        if rel_type is None:
            filters = {}
            for lid in lids:
                link = CLink(catalog, lid)
                if link.rel_type_hash not in filters:
                    filters[link.rel_type_hash] = link.rel_type.filter
                if filters[link.rel_type_hash](link):
                    yield link
        else:
            filter = rel_type.filter
            for lid in lids:
                link = CLink(catalog, lid)
                if filter(link):
                    yield link

    def getTargetsByRole(self, role, rel_type=None, catalog=None):
        links = self.iterLinksByRole(role, rel_type=rel_type, catalog=catalog)
        return [link.target for link in links]

    def iterTargetsByRole(self, role, rel_type=None, catalog=None):
        for link in self.iterLinksByRole(role, rel_type=rel_type, catalog=catalog):
            yield link.target
Ejemplo n.º 28
0
class ZODBGroupManager( BasePlugin ):

    """ PAS plugin for managing groups, and groups of groups in the ZODB
    """
    __implements__ = ( IGroupEnumerationPlugin
                     , IGroupsPlugin
                     )

    meta_type = 'ZODB Group Manager'

    security = ClassSecurityInfo()

    def __init__(self, id, title=None):

        self._id = self.id = id
        self.title = title
        self._groups = OOBTree()
        self._principal_groups = OOBTree()

    #
    #   IGroupEnumerationPlugin implementation
    #
    security.declarePrivate( 'enumerateGroups' )
    def enumerateGroups( self
                        , id=None
                        , title=None
                        , exact_match=False
                        , sort_by=None
                        , max_results=None
                        , **kw
                        ):

        """ See IGroupEnumerationPlugin.
        """
        group_info = []
        group_ids = []
        plugin_id = self.getId()

        if isinstance( id, str ):
            id = [ id ]

        if isinstance( title, str ):
            title = [ title ]

        if exact_match and ( id or title ):

            if id:
                group_ids.extend( id )
            elif title:
                group_ids.extend( title )

        if group_ids:
            group_filter = None

        else:   # Searching
            group_ids = self.listGroupIds()
            group_filter = _ZODBGroupFilter( id, title, **kw )

        for group_id in group_ids:

            if self._groups.get( group_id, None ):
                e_url = '%s/manage_groups' % self.getId()
                p_qs = 'group_id=%s' % group_id
                m_qs = 'group_id=%s&assign=1' % group_id

                info = {}
                info.update( self._groups[ group_id ] )
                
                info[ 'pluginid' ] = plugin_id
                info[ 'properties_url' ] = '%s?%s' % ( e_url, p_qs )
                info[ 'members_url' ] = '%s?%s' % ( e_url, m_qs )
                
                if not group_filter or group_filter( info ):
                    group_info.append( info )

        return tuple( group_info )

    #
    #   IGroupsPlugin implementation
    #
    security.declarePrivate( 'getGroupsForPrincipal' )
    def getGroupsForPrincipal( self, principal, request=None ):

        """ See IGroupsPlugin.
        """
        return tuple( self._principal_groups.get( principal.getId(), () ) )

    #
    #   (notional)IZODBGroupManager interface
    #
    security.declareProtected( ManageGroups, 'listGroupIds' )
    def listGroupIds( self ):

        """ -> ( group_id_1, ... group_id_n )
        """
        return self._groups.keys()

    security.declareProtected( ManageGroups, 'listGroupInfo' )
    def listGroupInfo( self ):

        """ -> ( {}, ...{} )

        o Return one mapping per group, with the following keys:

          - 'id' 
        """
        return self._groups.values()

    security.declareProtected( ManageGroups, 'getGroupInfo' )
    def getGroupInfo( self, group_id ):

        """ group_id -> {}
        """
        return self._groups[ group_id ]

    security.declarePrivate( 'addGroup' )
    def addGroup( self, group_id, title=None, description=None ):

        """ Add 'group_id' to the list of groups managed by this object.

        o Raise KeyError on duplicate.
        """
        if self._groups.get( group_id ) is not None:
            raise KeyError, 'Duplicate group ID: %s' % group_id

        self._groups[ group_id ] = { 'id' : group_id
                                   , 'title' : title
                                   , 'description' : description
                                   }

    security.declarePrivate( 'updateGroup' )
    def updateGroup( self, group_id, title, description ):

        """ Update properties for 'group_id'

        o Raise KeyError if group_id doesn't already exist.
        """
        self._groups[ group_id ].update({ 'title' : title
                                        , 'description' : description
                                        })
        self._groups[ group_id ] = self._groups[ group_id ]

    security.declarePrivate( 'removeGroup' )
    def removeGroup( self, group_id ):

        """ Remove 'role_id' from the list of roles managed by this
            object, removing assigned members from it before doing so.

        o Raise KeyError if 'group_id' doesn't already exist.
        """
        for principal_id in self._principal_groups.keys():
            self.removePrincipalFromGroup( principal_id, group_id )
        del self._groups[ group_id ]

    #
    #   Group assignment API
    #
    security.declareProtected( ManageGroups, 'listAvailablePrincipals' )
    def listAvailablePrincipals( self, group_id, search_name ):

        """ Return a list of principal IDs to that can belong to the group.

        o If supplied, 'search_name' constrains the principal IDs;  if not,
          return empty list.

        o Omit principals with existing assignments.
        """
        result = []

        if search_name:  # don't bother searching if no criteria

            parent = aq_parent( self )

            for info in parent.searchPrincipals( max_results=20
                                               , sort_by='id'
                                               , name=search_name
                                               , exact_match=False
                                               ):
                id = info[ 'id' ]
                title = info.get( 'title', id )
                if ( group_id not in self._principal_groups.get( id, () )
                 and group_id != id ):
                    result.append( ( id, title ) )
        
        return result

    security.declareProtected( ManageGroups, 'listAssignedPrincipals' )
    def listAssignedPrincipals( self, group_id ):

        """ Return a list of principal IDs belonging to a group.
        """
        result = []

        for k, v in self._principal_groups.items():
            if group_id in v:
                # should be one and only one mapping to 'k'

                parent = aq_parent( self )
                info = parent.searchPrincipals( id=k, exact_match=True )
                assert( len( info ) == 1 )
                result.append( ( k, info[0].get( 'title', k ) ) )
        
        return result

    security.declareProtected( ManageGroups, 'addPrincipalToGroup' )
    def addPrincipalToGroup( self, principal_id, group_id ):

        """ Add a principal to a group.

        o Return a boolean indicating whether a new assignment was created.

        o Raise KeyError if 'group_id' is unknown.
        """
        group_info = self._groups[ group_id ] # raise KeyError if unknown!

        current = self._principal_groups.get( principal_id, () )
        already = group_id in current

        if not already:
            new = current + ( group_id, )
            self._principal_groups[ principal_id ] = new

        return not already

    security.declareProtected( ManageGroups, 'removePrincipalFromGroup' )
    def removePrincipalFromGroup( self, principal_id, group_id ):

        """ Remove a prinicpal from from a group.

        o Return a boolean indicating whether the principal was already 
          a member of the group.

        o Raise KeyError if 'group_id' is unknown.

        o Ignore requests to remove a principal if not already a member
          of the group.
        """
        group_info = self._groups[ group_id ] # raise KeyError if unknown!

        current = self._principal_groups.get( principal_id, () )
        new = tuple( [ x for x in current if x != group_id ] )
        already = current != new

        if already:
            self._principal_groups[ principal_id ] = new

        return already

    #
    #   ZMI
    #
    manage_options = ( ( { 'label': 'Groups', 
                           'action': 'manage_groups', }
                         ,
                       )
                     + BasePlugin.manage_options
                     )

    security.declarePublic( 'manage_widgets' )
    manage_widgets = PageTemplateFile( 'www/zuWidgets'
                                     , globals()
                                     , __name__='manage_widgets'
                                     )

    security.declareProtected( ManageGroups, 'manage_groups' )
    manage_groups = PageTemplateFile( 'www/zgGroups'
                                    , globals()
                                    , __name__='manage_groups'
                                    )

    security.declareProtected( ManageGroups, 'manage_twoLists' )
    manage_twoLists = PageTemplateFile( '../www/two_lists'
                                      , globals()
                                      , __name__='manage_twoLists'
                                      )

    security.declareProtected( ManageGroups, 'manage_addGroup' )
    def manage_addGroup( self
                       , group_id
                       , title=None
                       , description=None
                       , RESPONSE=None
                       ):
        """ Add a group via the ZMI.
        """
        self.addGroup( group_id, title, description )

        message = 'Group+added'

        if RESPONSE is not None:
            RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s'
                             % ( self.absolute_url(), message )
                             )

    security.declareProtected( ManageGroups, 'manage_updateGroup' )
    def manage_updateGroup( self
                          , group_id
                          , title
                          , description
                          , RESPONSE=None
                          ):
        """ Update a group via the ZMI.
        """
        self.updateGroup( group_id, title, description )

        message = 'Group+updated'

        if RESPONSE is not None:
            RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s'
                             % ( self.absolute_url(), message )
                             )

    security.declareProtected( ManageGroups, 'manage_removeGroups' )
    def manage_removeGroups( self
                           , group_ids
                           , RESPONSE=None
                           ):
        """ Remove one or more groups via the ZMI.
        """
        group_ids = filter( None, group_ids )

        if not group_ids:
            message = 'no+groups+selected'

        else:
        
            for group_id in group_ids:
                self.removeGroup( group_id )

            message = 'Groups+removed'

        if RESPONSE is not None:
            RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s'
                             % ( self.absolute_url(), message )
                             )

    security.declareProtected( ManageGroups, 'manage_addPrincipalsToGroup' )
    def manage_addPrincipalsToGroup( self
                                   , group_id
                                   , principal_ids
                                   , RESPONSE=None
                                   ):
        """ Add one or more principals to a group via the ZMI.
        """
        assigned = []

        for principal_id in principal_ids:
            if self.addPrincipalToGroup( principal_id, group_id ):
                assigned.append( principal_id )

        if not assigned:
            message = 'Principals+already+members+of+%s' % group_id
        else:
            message = '%s+added+to+%s' % ( '+'.join( assigned )
                                         , group_id
                                         )

        if RESPONSE is not None:
            RESPONSE.redirect( ( '%s/manage_groups?group_id=%s&assign=1'
                               + '&manage_tabs_message=%s'
                               ) % ( self.absolute_url(), group_id, message )
                             )

    security.declareProtected( ManageGroups
                             , 'manage_removePrincipalsFromGroup' 
                             )
    def manage_removePrincipalsFromGroup( self
                                        , group_id
                                        , principal_ids
                                        , RESPONSE=None
                                        ):
        """ Remove one or more principals from a group via the ZMI.
        """
        removed = []
        
        for principal_id in principal_ids:
            if self.removePrincipalFromGroup( principal_id, group_id ):
                removed.append( principal_id )

        if not removed:
            message = 'Principals+not+in+group+%s' % group_id
        else:
            message = 'Principals+%s+removed+from+%s' % ( '+'.join( removed )
                                                        , group_id
                                                        )

        if RESPONSE is not None:
            RESPONSE.redirect( ( '%s/manage_groups?group_id=%s&assign=1'
                               + '&manage_tabs_message=%s'
                               ) % ( self.absolute_url(), group_id, message )
                             )
class TokenStorage(UniqueObject, SimpleItem, persistent.Persistent):
    isPrincipiaFolderish = True  # Show up in the ZMI
    security = ClassSecurityInfo()
    meta_type = "TokenStorage"
    id = "onetimetoken_storage"

    _timedelta = 504  # three weeks

    def __init__(self, request):
        self._tokens = OOBTree()

    def getTokens(self):
        """ Return all usernames and dates without tokens, read only
        """
        return self._tokens.values()

    security.declareProtected(ManageUsers, "setToken")

    def setToken(self, userId=None, generate_username_callback=None, generate_username_kwargs=None):
        """ Generate token for user or create one-time-user + token
        """
        token = ""
        m_tool = getToolByName(self, "portal_membership")
        if not userId:
            if generate_username_callback:
                userId = generate_username_callback(**(generate_username_kwargs or {}))
            else:
                userId = self.uniqueString()
            done = m_tool.acl_users.source_users.doAddUser(userId, self.uniqueString())
            assert done, "User could not be created for OneTimeToken!"

        expiry = str(self.expirationDate())
        token = self.uniqueString()

        self._tokens[token] = (userId, expiry)
        login = "******" % (userId, token)

        # encode the login string to make it url safe
        token = encodestring(login)

        return token

    security.declarePublic("verifyToken")

    def verifyToken(self, loginCode):
        """
        """
        try:
            userId, token = decodestring(loginCode).split(":")
        except:
            raise TokenError("InvalidLoginCodeError")

        try:
            u, expiry = self._tokens[token]
        except KeyError:
            raise TokenError("InvalidTokenError")

        if self.expired(expiry):
            raise TokenError("ExpiredExpiryError")

        if not u == userId:
            raise TokenError("InvalidUserError")

        del self._tokens[token]

        return u

    security.declarePublic("deleteTemporaryUser")

    def deleteTemporaryUser(self, userId):
        """
        """
        m_tool = getToolByName(self, "portal_membership")
        return m_tool.acl_users.source_users.doDeleteUser(userId)

    security.declarePrivate("uniqueString")

    def uniqueString(self):
        """Returns a string that is random and unguessable, or at
        least as close as possible."""
        # this is the informal UUID algorithm of
        # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213761
        # by Carl Free Jr
        t = long(time.time() * 1000)
        r = long(random.random() * 100000000000000000L)
        try:
            a = socket.gethostbyname(socket.gethostname())
        except:
            # if we can't get a network address, just imagine one
            a = random.random() * 100000000000000000L
        data = str(t) + " " + str(r) + " " + str(a)  # +' '+str(args)
        data = md5.md5(data).hexdigest()
        return str(data)

    security.declarePrivate("expirationDate")

    def expirationDate(self):
        """Returns a DateTime for exipiry of a request from the
        current time.

        This is used by housekeeping methods (like clearEpired)
        and stored in reset request records."""
        if not hasattr(self, "_timedelta"):
            self._timedelta = 168
        try:
            if isinstance(self._timedelta, datetime.timedelta):
                expire = datetime.datetime.utcnow() + self._timedelta
                return DateTime(expire.year, expire.month, expire.day, expire.hour, expire.minute, expire.second, "UTC")
        except NameError:
            pass  # that's okay, it must be a number of hours...
        expire = time.time() + self._timedelta * 3600  # 60 min/hr * 60 sec/min
        return DateTime(expire)

    security.declarePrivate("expired")

    def expired(self, datetime, now=None):
        """Tells whether a DateTime or timestamp 'datetime' is expired
        with regards to either 'now', if provided, or the current
        time."""
        if not now:
            now = DateTime()
        return now.greaterThanEqualTo(datetime)

    security.declarePrivate("clearExpired")

    def clearExpired(self, days=0):
        """Destroys all expired reset request records.
        Parameter 'days' controls how many days past expired it must be to clear token.
        """
        for token, record in self._tokens.items():
            stored_user, expiry = record
            if self.expired(DateTime(expiry), DateTime() - days):
                del self._tokens[token]
                self.deleteTemporaryUser(stored_user)
Ejemplo n.º 30
0
class ZODBGroupManager(BasePlugin):
    """ PAS plugin for managing groups, and groups of groups in the ZODB
    """
    meta_type = 'ZODB Group Manager'

    security = ClassSecurityInfo()

    def __init__(self, id, title=None):

        self._id = self.id = id
        self.title = title
        self._groups = OOBTree()
        self._principal_groups = OOBTree()

    #
    #   IGroupEnumerationPlugin implementation
    #
    security.declarePrivate('enumerateGroups')

    def enumerateGroups(self,
                        id=None,
                        title=None,
                        exact_match=False,
                        sort_by=None,
                        max_results=None,
                        **kw):
        """ See IGroupEnumerationPlugin.
        """
        group_info = []
        group_ids = []
        plugin_id = self.getId()

        if isinstance(id, str):
            id = [id]

        if isinstance(title, str):
            title = [title]

        if exact_match and (id or title):

            if id:
                group_ids.extend(id)
            elif title:
                group_ids.extend(title)

        if group_ids:
            group_filter = None

        else:  # Searching
            group_ids = self.listGroupIds()
            group_filter = _ZODBGroupFilter(id, title, **kw)

        for group_id in group_ids:

            if self._groups.get(group_id, None):
                e_url = '%s/manage_groups' % self.getId()
                p_qs = 'group_id=%s' % group_id
                m_qs = 'group_id=%s&assign=1' % group_id

                info = {}
                info.update(self._groups[group_id])

                info['pluginid'] = plugin_id
                info['properties_url'] = '%s?%s' % (e_url, p_qs)
                info['members_url'] = '%s?%s' % (e_url, m_qs)

                info['id'] = '%s%s' % (self.prefix, info['id'])

                if not group_filter or group_filter(info):
                    group_info.append(info)

        return tuple(group_info)

    #
    #   IGroupsPlugin implementation
    #
    security.declarePrivate('getGroupsForPrincipal')

    def getGroupsForPrincipal(self, principal, request=None):
        """ See IGroupsPlugin.
        """
        unadorned = self._principal_groups.get(principal.getId(), ())
        return tuple(['%s%s' % (self.prefix, x) for x in unadorned])

    #
    #   (notional)IZODBGroupManager interface
    #
    security.declareProtected(ManageGroups, 'listGroupIds')

    def listGroupIds(self):
        """ -> ( group_id_1, ... group_id_n )
        """
        return self._groups.keys()

    security.declareProtected(ManageGroups, 'listGroupInfo')

    def listGroupInfo(self):
        """ -> ( {}, ...{} )

        o Return one mapping per group, with the following keys:

          - 'id' 
        """
        return self._groups.values()

    security.declareProtected(ManageGroups, 'getGroupInfo')

    def getGroupInfo(self, group_id):
        """ group_id -> {}
        """
        return self._groups[group_id]

    security.declarePrivate('addGroup')

    def addGroup(self, group_id, title=None, description=None):
        """ Add 'group_id' to the list of groups managed by this object.

        o Raise KeyError on duplicate.
        """
        if self._groups.get(group_id) is not None:
            raise KeyError, 'Duplicate group ID: %s' % group_id

        self._groups[group_id] = {
            'id': group_id,
            'title': title,
            'description': description
        }

    security.declarePrivate('updateGroup')

    def updateGroup(self, group_id, title, description):
        """ Update properties for 'group_id'

        o Raise KeyError if group_id doesn't already exist.
        """
        self._groups[group_id].update({
            'title': title,
            'description': description
        })
        self._groups[group_id] = self._groups[group_id]

    security.declarePrivate('removeGroup')

    def removeGroup(self, group_id):
        """ Remove 'role_id' from the list of roles managed by this
            object, removing assigned members from it before doing so.

        o Raise KeyError if 'group_id' doesn't already exist.
        """
        for principal_id in self._principal_groups.keys():
            self.removePrincipalFromGroup(principal_id, group_id)
        del self._groups[group_id]

    #
    #   Group assignment API
    #
    security.declareProtected(ManageGroups, 'listAvailablePrincipals')

    def listAvailablePrincipals(self, group_id, search_id):
        """ Return a list of principal IDs to that can belong to the group.

        o If supplied, 'search_id' constrains the principal IDs;  if not,
          return empty list.

        o Omit principals with existing assignments.
        """
        result = []

        if search_id:  # don't bother searching if no criteria

            parent = aq_parent(self)

            for info in parent.searchPrincipals(max_results=20,
                                                sort_by='id',
                                                id=search_id,
                                                exact_match=False):
                id = info['id']
                title = info.get('title', id)
                if (group_id not in self._principal_groups.get(id, ())
                        and group_id != id):
                    result.append((id, title))

        return result

    security.declareProtected(ManageGroups, 'listAssignedPrincipals')

    def listAssignedPrincipals(self, group_id):
        """ Return a list of principal IDs belonging to a group.
        """
        result = []

        for k, v in self._principal_groups.items():
            if group_id in v:
                # should be one and only one mapping to 'k'

                parent = aq_parent(self)
                info = parent.searchPrincipals(id=k, exact_match=True)
                assert (len(info) in (0, 1))
                if len(info) == 0:
                    title = '<%s: not found>' % k
                else:
                    title = info[0].get('title', k)
                result.append((k, title))

        return result

    security.declareProtected(ManageGroups, 'addPrincipalToGroup')

    def addPrincipalToGroup(self, principal_id, group_id, REQUEST=None):
        """ Add a principal to a group.

        o Return a boolean indicating whether a new assignment was created.

        o Raise KeyError if 'group_id' is unknown.
        """
        group_info = self._groups[group_id]  # raise KeyError if unknown!

        current = self._principal_groups.get(principal_id, ())
        already = group_id in current

        if not already:
            new = current + (group_id, )
            self._principal_groups[principal_id] = new

        return not already

    addPrincipalToGroup = postonly(addPrincipalToGroup)

    security.declareProtected(ManageGroups, 'removePrincipalFromGroup')

    def removePrincipalFromGroup(self, principal_id, group_id, REQUEST=None):
        """ Remove a prinicpal from from a group.

        o Return a boolean indicating whether the principal was already 
          a member of the group.

        o Raise KeyError if 'group_id' is unknown.

        o Ignore requests to remove a principal if not already a member
          of the group.
        """
        group_info = self._groups[group_id]  # raise KeyError if unknown!

        current = self._principal_groups.get(principal_id, ())
        new = tuple([x for x in current if x != group_id])
        already = current != new

        if already:
            self._principal_groups[principal_id] = new

        return already

    removePrincipalFromGroup = postonly(removePrincipalFromGroup)

    #
    #   ZMI
    #
    manage_options = (({
        'label': 'Groups',
        'action': 'manage_groups',
    }, ) + BasePlugin.manage_options)

    security.declarePublic('manage_widgets')
    manage_widgets = PageTemplateFile('www/zuWidgets',
                                      globals(),
                                      __name__='manage_widgets')

    security.declareProtected(ManageGroups, 'manage_groups')
    manage_groups = PageTemplateFile('www/zgGroups',
                                     globals(),
                                     __name__='manage_groups')

    security.declareProtected(ManageGroups, 'manage_twoLists')
    manage_twoLists = PageTemplateFile('../www/two_lists',
                                       globals(),
                                       __name__='manage_twoLists')

    security.declareProtected(ManageGroups, 'manage_addGroup')

    def manage_addGroup(self,
                        group_id,
                        title=None,
                        description=None,
                        RESPONSE=None):
        """ Add a group via the ZMI.
        """
        self.addGroup(group_id, title, description)

        message = 'Group+added'

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_groups?manage_tabs_message=%s' %
                              (self.absolute_url(), message))

    security.declareProtected(ManageGroups, 'manage_updateGroup')

    def manage_updateGroup(self, group_id, title, description, RESPONSE=None):
        """ Update a group via the ZMI.
        """
        self.updateGroup(group_id, title, description)

        message = 'Group+updated'

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_groups?manage_tabs_message=%s' %
                              (self.absolute_url(), message))

    security.declareProtected(ManageGroups, 'manage_removeGroups')

    def manage_removeGroups(self, group_ids, RESPONSE=None, REQUEST=None):
        """ Remove one or more groups via the ZMI.
        """
        group_ids = filter(None, group_ids)

        if not group_ids:
            message = 'no+groups+selected'

        else:

            for group_id in group_ids:
                self.removeGroup(group_id)

            message = 'Groups+removed'

        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_groups?manage_tabs_message=%s' %
                              (self.absolute_url(), message))

    manage_removeGroups = postonly(manage_removeGroups)

    security.declareProtected(ManageGroups, 'manage_addPrincipalsToGroup')

    def manage_addPrincipalsToGroup(self,
                                    group_id,
                                    principal_ids,
                                    RESPONSE=None,
                                    REQUEST=None):
        """ Add one or more principals to a group via the ZMI.
        """
        assigned = []

        for principal_id in principal_ids:
            if self.addPrincipalToGroup(principal_id, group_id):
                assigned.append(principal_id)

        if not assigned:
            message = 'Principals+already+members+of+%s' % group_id
        else:
            message = '%s+added+to+%s' % ('+'.join(assigned), group_id)

        if RESPONSE is not None:
            RESPONSE.redirect(('%s/manage_groups?group_id=%s&assign=1' +
                               '&manage_tabs_message=%s') %
                              (self.absolute_url(), group_id, message))

    manage_addPrincipalsToGroup = postonly(manage_addPrincipalsToGroup)

    security.declareProtected(ManageGroups, 'manage_removePrincipalsFromGroup')

    def manage_removePrincipalsFromGroup(self,
                                         group_id,
                                         principal_ids,
                                         RESPONSE=None,
                                         REQUEST=None):
        """ Remove one or more principals from a group via the ZMI.
        """
        removed = []

        for principal_id in principal_ids:
            if self.removePrincipalFromGroup(principal_id, group_id):
                removed.append(principal_id)

        if not removed:
            message = 'Principals+not+in+group+%s' % group_id
        else:
            message = 'Principals+%s+removed+from+%s' % ('+'.join(removed),
                                                         group_id)

        if RESPONSE is not None:
            RESPONSE.redirect(('%s/manage_groups?group_id=%s&assign=1' +
                               '&manage_tabs_message=%s') %
                              (self.absolute_url(), group_id, message))

    manage_removePrincipalsFromGroup = postonly(
        manage_removePrincipalsFromGroup)
Ejemplo n.º 31
0
class Calendar:
    """This class represents a calendar which is a set of days which contain
        information about which conferences whithin certain categories are
        happening for each of these days and for a certain access. This class
        allows to configure the date interval and the category set to be
        considered and provides operations which allow to know about what's
        happening on each of those days.

       Attributes:
        _aw - (accessControl.AccessWrapper) Information about the access for
            which the calendar will be built.
        _sDate - (datetime) Starting date for the calendar.
        _eDate - (datetime) Ending date for the calendar.
        _categList - (List) List of categories to be considered.
        _days - (OOBTree) Index of days which build up the calendar.
    """
    def __init__(self, aw, sDate, eDate, categList=[]):
        self._aw = aw
        self._tz = sDate.tzinfo
        self._sDate = sDate.replace(hour=0, minute=0, second=0, microsecond=0)
        self._eDate = eDate.replace(hour=23,
                                    minute=59,
                                    second=59,
                                    microsecond=0)
        self._categList = categList
        self._icons = {}
        self._days = None

    def getIcons(self):
        try:
            return self._icons
        except:
            self._icons = {}
            return {}

    def setIcons(self, categ):
        """Retrieves the list of icons in a given category tree
        """
        if categ.getIcon() != None:
            return [categ.getId()]
        res = []
        for subcat in categ.getSubCategoryList():
            res += self.setIcons(subcat)
        return res

    def getStartDate(self):
        return self._sDate

    def getEndDate(self):
        return self._eDate

    def getCategoryList(self):
        return self._categList

    def getLocator(self):
        """Returns the generic locator for the current object. This locator
            contains the folloing entries corresponding to values for which
            the calendar is configured:
                selCateg -> List of category ids.
                sDate -> Starting date.
                eDate -> Ending date.
        """
        l = Locators.Locator()
        ids = []
        for c in self.getCategoryList():
            ids.append(c.getId())
        l["selCateg"] = ids
        l["sDate"] = self.getStartDate().strftime("%Y-%m-%d")
        l["eDate"] = self.getStartDate().strftime("%Y-%m-%d")
        return l

    def _mapConferenceToDays(self, conf, categList):
        """Registers a given conference for the days on which it is taking place
            whithin the calendar date interval.

           Parameters:
            conf - (conference.Conference) Conference to be mapped.
            categList - (List) List of calendar categories in which the
                specified conference is found.
        """

        inc = timedelta(1)
        d = max(
            conf.getStartDate().astimezone(self._tz).replace(hour=0,
                                                             minute=0,
                                                             second=0),
            self.getStartDate().astimezone(self._tz).replace(hour=0,
                                                             minute=0,
                                                             second=0))
        ed = min(conf.getEndDate().astimezone(self._tz),
                 self.getEndDate().astimezone(self._tz))
        if ed > self.getEndDate():
            ed = self.getEndDate()

        while d <= ed:
            #norm_date=d.tzinfo.normalize(d)
            #norm_date=norm_date.replace(hour=0)
            norm_date = self.getNormDate(d)
            if not self._days.has_key(norm_date):
                self._days[norm_date] = Day(self, d)

            self._days[norm_date].addConference(conf, categList, self._tz)
            d += inc

    def _initialiseDays(self):
        """
        """
        self._days = OOBTree()
        res = set()
        self._categIdx = {}
        self._icons = {}
        catDayIdx = indexes.IndexesHolder().getIndex("categoryDate")
        for categ in self.getCategoryList():
            confs = catDayIdx.getObjectsInDays(categ.getId(),
                                               self.getStartDate(),
                                               self.getEndDate())
            for conf in confs:
                confId = conf.getId()
                if not self._categIdx.has_key(confId):
                    self._categIdx[confId] = []
                self._categIdx[confId].append(categ)
            res.update(confs)
        for conf in res:
            #getting icon from the nearest owner category
            owner = conf.getOwner()
            while owner != None and owner.getId() != "0":
                if owner.getIcon():
                    if self._icons.has_key(owner.getId()):
                        self._icons[owner.getId()].append(conf.getId())
                    else:
                        self._icons[owner.getId()] = [conf.getId()]
                    break
                owner = owner.getOwner()
            #mapping conf to days
            self._mapConferenceToDays(conf, self._categIdx[conf.getId()])

    def getNormDate(self, date):
        # we have to normalize, but as we are going over all the days, we have to keep the date
        # and just normalize the tzinfo.
        norm_date = date.tzinfo.normalize(date)
        norm_date = norm_date.replace(year=date.year,
                                      month=date.month,
                                      day=date.day,
                                      hour=0)
        return norm_date

    def getDay(self, date):
        if not self._days:
            self._initialiseDays()
        norm_date = self.getNormDate(date)
        if not self._days.has_key(norm_date):
            self._days[norm_date] = Day(self, date)
        return self._days[norm_date]

    def getDayList(self):
        inc = timedelta(1)
        d = self.getStartDate()
        l = []
        while d < self.getEndDate():
            l.append(self.getDay(d))
            d += inc
        return l

    def getConferenceCategories(self, conf):
        return self._categIdx[conf.getId()]

    def __str__(self):
        l = []
        if self._days:
            for day in self._days.values():
                l.append("%s" % day)
        str =  _("Calendar between %s and %s: %s")%(\
                            self.getStartDate().strftime("%Y-%m-%d"), \
                            self.getEndDate().strftime("%Y-%m-%d"), \
                            "\n\t\t".join(l) )
        return str
Ejemplo n.º 32
0
class ToManyContRelationship(ToManyRelationshipBase):
    """
    ToManyContRelationship is the ToMany side of a realtionship that
    contains its related objects (like the normal Zope ObjectManager)
    """

    meta_type = "ToManyContRelationship"

    security = ClassSecurityInfo()


    def __init__(self, id):
        """set our instance values"""
        self.id = id
        self._objects = OOBTree()


    def _safeOfObjects(self):
        """
        Try to safely return ZenPack objects rather than
        causing imports to fail.
        """
        objs = []
        for ob in self._objects.values():
            try:
                objs.append(ob.__of__(self))
            except AttributeError:
                log.info("Ignoring unresolvable object '%s'", str(ob))
        return objs

    def __call__(self):
        """when we are called return our related object in our aq context"""
        return self._safeOfObjects()


    def __getattr__(self, name):
        """look in the two object stores for related objects"""
        if '_objects' in self.__dict__:
            objects = self._objects
            if objects.has_key(name): return objects[name]
        raise AttributeError( "Unable to find the attribute '%s'" % name )


    def __hasattr__(self, name):
        """check to see if we have an object by an id
        this will fail if passed a short id and object is stored
        with fullid (ie: it is related not contained)
        use hasobject to get around this issue"""
        return self._objects.has_key(name)


    def hasobject(self, obj):
        "check to see if we have this object"
        return self._objects.get(obj.id) == obj


    def addRelation(self, obj):
        """Override base to run manage_afterAdd like ObjectManager"""
        if self._objects.has_key(obj.getId()):
            log.debug("obj %s already exists on %s", obj.getPrimaryId(),
                        self.getPrimaryId())

        notify(ObjectWillBeAddedEvent(obj, self, obj.getId()))
        ToManyRelationshipBase.addRelation(self, obj)
        obj = obj.__of__(self)
        o = self._getOb(obj.id)
        notify(ObjectAddedEvent(o, self, obj.getId()))


    def _setObject(self,id,object,roles=None,user=None,set_owner=1):
        """ObjectManager interface to add contained object."""
        unused(user, roles, set_owner)
        object.__primary_parent__ = aq_base(self)
        self.addRelation(object)
        return object.getId()


    def manage_afterAdd(self, item, container):
        # Don't do recursion anymore, a subscriber does that.
        pass
    manage_afterAdd.__five_method__ = True

    def manage_afterClone(self, item):
        # Don't do recursion anymore, a subscriber does that.
        pass
    manage_afterClone.__five_method__ = True

    def manage_beforeDelete(self, item, container):
        # Don't do recursion anymore, a subscriber does that.
        pass
    manage_beforeDelete.__five_method__ = True

    def _add(self,obj):
        """add an object to one side of a ToManyContRelationship.
        """
        id = obj.id
        if self._objects.has_key(id):
            raise RelationshipExistsError
        v=checkValidId(self, id)
        if v is not None: id=v
        self._objects[id] = aq_base(obj)
        obj = aq_base(obj).__of__(self)


    def _remove(self, obj=None, suppress_events=False):
        """remove object from our side of a relationship"""
        if obj: objs = [obj]
        else: objs = self.objectValuesAll()
        if not suppress_events:
            for robj in objs:
                notify(ObjectWillBeRemovedEvent(robj, self, robj.getId()))
        if obj:
            id = obj.id
            if not self._objects.has_key(id):
                raise ObjectNotFound(
                    "object %s not found on %s" % (
                    obj.getPrimaryId(), self.getPrimaryId()))
            del self._objects[id]
        else:
            self._objects = OOBTree()
            self.__primary_parent__._p_changed = True
        if not suppress_events:
            for robj in objs:
                notify(ObjectRemovedEvent(robj, self, robj.getId()))


    def _remoteRemove(self, obj=None):
        """remove an object from the far side of this relationship
        if no object is passed in remove all objects"""
        if obj:
            if not self._objects.has_key(obj.id):
                raise ObjectNotFound("object %s not found on %s" % (
                    obj.getPrimaryId(), self.getPrimaryId()))
            objs = [obj]
        else: objs = self.objectValuesAll()
        remoteName = self.remoteName()
        for obj in objs:
            rel = getattr(obj, remoteName)
            try:
                rel._remove(self.__primary_parent__)
            except ObjectNotFound:
                message = log_tb(sys.exc_info())
                log.error('Remote remove failed. Run "zenchkrels -r -x1". ' + message)
                continue


    def _getOb(self, id, default=zenmarker):
        """look up in our local store and wrap in our aq_chain"""
        if self._objects.has_key(id):
            return self._objects[id].__of__(self)
        elif default == zenmarker:
            raise AttributeError( "Unable to find %s" % id )
        return default


    security.declareProtected('View', 'objectIds')
    def objectIds(self, spec=None):
        """only return contained objects"""
        if spec:
            if isinstance(spec,basestring): spec=[spec]
            return [obj.id for obj in self._objects.values() \
                        if obj.meta_type in spec]
        return [ k for k in self._objects.keys() ]
    objectIdsAll = objectIds


    security.declareProtected('View', 'objectValues')
    def objectValues(self, spec=None):
        """override to only return owned objects for many to many rel"""
        if spec:
            if isinstance(spec,basestring): spec=[spec]
            return [ob.__of__(self) for ob in self._objects.values() \
                        if ob.meta_type in spec]
        return self._safeOfObjects()
    security.declareProtected('View', 'objectValuesAll')
    objectValuesAll = objectValues


    def objectValuesGen(self):
        """Generator that returns all related objects."""
        return (obj.__of__(self) for obj in self._objects.values())


    def objectItems(self, spec=None):
        """over ride to only return owned objects for many to many rel"""
        if spec:
            if isinstance(spec,basestring): spec=[spec]
            return [(key,value.__of__(self)) \
                for (key,value) in self._objects.items() \
                    if value.meta_type in spec]
        return [(key,value.__of__(self)) \
                    for (key,value) in self._objects.items()]
    objectItemsAll = objectItems


#FIXME - need to make this work
#    def all_meta_types(self, interfaces=None):
#        mts = []
#        for mt in ToManyRelationshipBase.all_meta_types(self, interfaces):
#            if (mt.has_key('instance') and mt['instance']):
#                for cl in self.sub_classes:
#                    if checkClass(mt['instance'], cl):
#                        mts.append(mt)
#        return mts


    def _getCopy(self, container):
        """
        make new relation add copies of contained objs
        and refs if the relation is a many to many
        """
        rel = self.__class__(self.id)
        rel.__primary_parent__ = container
        rel = rel.__of__(container)
        norelcopy = getattr(self, 'zNoRelationshipCopy', [])
        if self.id in norelcopy: return rel
        for oobj in self.objectValuesAll():
            cobj = oobj._getCopy(rel)
            rel._setObject(cobj.id, cobj)
        return rel

    def checkValidId(self, id):
        """
        Is this a valid id for this container?
        """
        try:
            checkValidId(self, id)
        except:
            raise
        else:
            return True

    def exportXml(self, ofile, ignorerels=[]):
        """Return an xml representation of a ToManyContRelationship
        <tomanycont id='interfaces'>
            <object id='hme0'
                module='Products.Confmon.IpInterface' class='IpInterface'>
                <property></property> etc....
            </object>
        </tomanycont>
        """
        if self.countObjects() == 0: return
        ofile.write("<tomanycont id='%s'>\n" % self.id)
        for obj in self.objectValues():
            obj.exportXml(ofile, ignorerels)
        ofile.write("</tomanycont>\n")


    def checkRelation(self, repair=False):
        """Check to make sure that relationship bidirectionality is ok.
        """
        if len(self._objects):
            log.debug("checking relation: %s", self.id)
        else:
            return

        # look for objects that don't point back to us
        # or who should no longer exist in the database
        remoteName = self.remoteName()
        parentObject = self.getPrimaryParent()
        for obj in self._objects.values():
            if not hasattr(obj, remoteName):
                path = parentObject.getPrimaryUrlPath()
                if repair:
                    log.warn("Deleting %s object '%s' relation '%s' (missing remote relation '%s')",
                             path, obj, self.id, remoteName)
                    self._remove(obj, True)
                    continue
                else:
                    msg = "%s object '%s' relation '%s' missing remote relation '%s'" % (
                             path, obj, self.id, remoteName)
                    raise AttributeError(msg)

            rrel = getattr(obj, remoteName)
            if not rrel.hasobject(parentObject):
                log.error("remote relation %s doesn't point back to %s",
                                rrel.getPrimaryId(), self.getPrimaryId())
                if repair:
                    log.warn("reconnecting relation %s to relation %s",
                            rrel.getPrimaryId(),self.getPrimaryId())
                    rrel._add(parentObject)
Ejemplo n.º 33
0
class Index(Persistent, object):

    implements(IIndex)

    ranking_method = defaults['ranking_method']

    def __init__(self, **kw):

        # perform argument check first
        illegal_args = [k for k in kw.keys() if not k in defaults.keys()]
        if illegal_args:
            raise ValueError('Unknown parameters: %s' %
                             ', '.join(illegal_args))

        # setup preferences using default args (preferences are stored as
        # attributes of the index instance

        for k, v in defaults.items():
            v = kw.get(k, v)
            setattr(self, k, v)

        self.clear()

    def clear(self):

        # lexicon & storage
        self._lexicon = createObject(self.lexicon, self.languages)

        # build either a mapping of storages when using dedicated storages
        # otherwise use a single storage

        self._feature_ranking = False  # this index supports ranking?

        if self.dedicated_storage:
            self._storage = OOBTree()
            for f in self.fields:
                self._storage[f] = createObject(self.storage)
                self._feature_ranking = IStorageWithTermFrequency.providedBy(
                    self._storage[f])
        else:
            self._storage = createObject(self.storage)
            self._feature_ranking = IStorageWithTermFrequency.providedBy(
                self._storage)

    def getLexicon(self):
        """ return the lexicon """
        return self._lexicon

    def getStorage(self, field):
        """ return the storage """

        if self.dedicated_storage:
            try:
                return self._storage[field]
            except KeyError:
                raise ValueError("No such storage for field '%s'" % field)
        else:
            return self._storage

    def getSettings(self):
        """ returns a mapping contains the indexes preferences """
        from copy import copy
        d = {}
        for k in defaults.keys():
            d[k] = copy(getattr(self, k))
        return d

    def index_object(self, obj, docid):
        """ index a given object under a given document id """

        # Call the content extractor which is responsible for
        # the extraction of all related content parts from obj
        # based on the object type and the index settings.
        # The extractor should return a dictionary that maps
        # all fields of the index to a dictionary the following
        # key-value pairs:
        # 'language' -> language
        # 'content' -> content of particular field in obj as unicode
        #              string

        try:
            indexable_content = extract_content(
                self.fields,
                obj,
                default_encoding=self.default_encoding,
                default_language=self.languages[0])
        except:
            handle_exc('extract_content failed', obj, sys.exc_info())
            return False

        if indexable_content is None or not indexable_content:
            return False

        # now iterate over all fields and pass all field related content
        # through the indexer pipeline

        all_wordids = []  # we need this only if dedicated_storage == False

        for field in [
                f for f in self.fields if f in indexable_content.getFields()
        ]:
            wordids = []

            for info in indexable_content.getFieldData(field):
                content = info['content']

                if not isinstance(content, unicode):
                    raise ValueError('Content must be unicode: %s' %
                                     repr(content))

                # If a document has an unknown language (other than the ones configured
                # for the index), an exception will be raised or the content is
                # indexed under the first configured language

                language = info['language']

                if not language in self.languages:
                    if self.index_unknown_languages:
                        language = self.languages[0]
                    else:
                        raise ValueError(
                            'Unsupported language: %s (allowed: %s)' %
                            (language, ', '.join(self.languages)))

                # run content through the pipline (splitter, stopword remover, normalizer etc)
                words = self._process_words(content, language)

                # now obtain wordids from the lexicon for all words
                wordids.extend(self._lexicon.insertWords(words, language))

            # If we have dedicated storages for every field then we must insert the
            # wordids here otherwise we collect all wordids and insert them into
            # the default storage. This implies that one should use dedicated storages
            # when indexing multiple fields. If you index multiple fields without
            # dedicated storage you will not be able to search by-field.

            if self.dedicated_storage:
                try:
                    old_ids = self._storage[field].getWordIdsForDocId(docid)
                except StorageException:
                    old_ids = []

                if old_ids != wordids:
                    self._storage[field].insertDocument(docid, wordids)
            else:
                all_wordids.extend(wordids)

        # Insert everything into the default storage when not using dedicated
        # storages
        if not self.dedicated_storage:
            try:
                old_ids = self._storage.getWordIdsForDocId(docid)
            except StorageException:
                old_ids = []

            if old_ids != all_wordids:
                self._storage.insertDocument(docid, all_wordids)

        return True

    def _process_words(self, content, language):
        """ implements the processing pipeline """

        # first normalize content string
        if self.use_normalizer:
            normalizer = getUtility(INormalizer)
            content = normalizer.process(content, language)

        # now create a new splitter
        splitter = createObject(
            self.splitter,
            casefolding=self.splitter_casefolding,
            separator=self.splitter_additional_chars,
            maxlen=self.splitter_max_length,
        )

        # and split unicode content into list of unicode strings
        words = splitter.split(content)

        # now filter out all stopwords
        if self.use_stopwords:
            sw_utility = getUtility(IStopwords)
            words = sw_utility.process(words, language)

        # Stem words if required. If no stemmer for 'language' is available
        # then do not stem
        if self.use_stemmer:
            S = getStemmer(language)
            if S:
                words = S.stem(words)

        return words

    def unindex_object(self, docid):
        """ remove a document given its document id from the index """

        if self.dedicated_storage:
            for field in self.fields:
                self._storage[field].removeDocument(docid)
        else:
            self._storage.removeDocument(docid)

    def _prepare_query(self, query, language):
        """ performs similar transformations as _process_words() but
            only for a query. So we don't need the splitter etc.
        """

        # to lowercase if necessary
        if self.splitter_casefolding:
            query = query.lower()

        # normalize query string
        if self.use_normalizer:
            normalizer = getUtility(INormalizer)
            query = normalizer.process(query, language)

        return query

    query_options = ('parser', 'language', 'field', 'search_all_fields',
                     'autoexpand', 'similarity_ratio', 'thesaurus', 'ranking',
                     'ranking_maxhits')

    def search(self, query, **kw):
        """ Perform a query against the index. Valid query options are:

            'parser' -- named utility implementing IParser
            'language' -- language to be used to lookup words from the lexicon
            'field' -- perform searches against a configured index field
            'autoexpand' -- off|always|on_miss (see below)
        """

        # queries must be unicode
        if not isinstance(query, unicode):
            raise ValueError('Query must be unicode string')

        # First check query options
        for k in kw.keys():
            if not k in self.query_options:
                raise ValueError(
                    'Unknown option: %s (supported query options: %s)' %
                    (k, ', '.join(self.query_options)))

        # obtain parser ID (which is the name of named utility implementing IParser)
        parser_id = kw.get('parser', self.query_parser)

        # determine query language
        language = kw.get('language', self.languages[0])
        if not language in self.languages:
            raise ValueError(
                'Unsupported language: %s (supported languages: %s)' %
                (language, ', '.join(self.languages)))

        # check if field is known to the index
        field = kw.get('field')
        search_all_fields = kw.get('search_all_fields')
        if field and search_all_fields:
            raise ValueError('Cannot specify field and search_all_fields')
        if search_all_fields:
            if not self.dedicated_storage:
                raise ValueError(
                    'search_all_fields cannot be used without dedicated '
                    'storage.')
            search_fields = self.fields
        else:
            if not field:
                field = self.fields[0]
            if field not in self.fields:
                raise ValueError('Unknown field: %s (known fields: %s)' %
                                 (field, ', '.join(self.fields)))
            search_fields = [field]

        # perform optional cosine ranking after searching
        ranking = bool(kw.get('ranking', self.ranking))
        if ranking and not self._feature_ranking:
            raise ValueError(
                "The storage used for this index does not support relevance ranking"
            )

        # Limit *ranked* result set to at most XXX hits
        ranking_maxhits = kw.get('ranking_maxhits', 50)
        if not isinstance(ranking_maxhits, int):
            raise ValueError('"ranking_maxhits" must be an integer')
        if kw.has_key('ranking_maxhits') and not ranking:
            raise ValueError(
                'Specify "ranking_maxhits" only with having set ranking=True')

        # autoexpansion of query terms
        # 'off' -- expand never
        # 'always' -- expand always
        # 'on_miss' -- expand only for not-found terms in the query string
        autoexpand = kw.get('autoexpand', self.autoexpand)
        if not autoexpand in ('off', 'always', 'on_miss'):
            raise ValueError(
                '"autoexpand" must either be "off", "always" or "on_miss"')

        # Use a sequence of configured thesauri (identified by their configured name)
        # for additional lookup of terms
        thesaurus = kw.get('thesaurus', [])
        if isinstance(thesaurus, str):
            thesaurus = (thesaurus, )
        if not isinstance(thesaurus, (list, tuple)):
            raise ValueError(
                '"thesaurus" must be list or tuple of configured thesaurus ids'
            )

        # Similarity ratio (measured as Levenshtein distance)
        similarity_ratio = float(kw.get('similarity_ratio', 0.75))
        if similarity_ratio < 0.0 or similarity_ratio > 1.0:
            raise ValueError(
                'similarity_ratio must been 0.0 and 1.0 (value %f)' %
                similarity_ratio)

        # obtain a parser (registered  as named utility)
        parser = getUtility(IParser, parser_id)

        # run query string through normalizer, case normalizer etc.
        query = self._prepare_query(query, language)

        # create a tree of nodes
        parsed_query = parser.parse(query)

        if not parsed_query:
            raise ValueError('No query specified')

        # Post-filter for stopwords. We need to perform this
        # outside the query parser because the lex/yacc-based query
        # parser implementation can't be used in a reasonable way
        # to deal with such additional functionality.

        if self.use_stopwords:
            sw_utility = getUtility(IStopwords)
            stopwords = sw_utility.stopwordsForLanguage(language)

            if stopwords:
                # The stopword remover removes WordNodes representing
                # a stopword *in-place*
                stopword_remover(parsed_query, stopwords)

        # Split word nodes with the splitter
        splitter = createObject(
            self.splitter,
            casefolding=self.splitter_casefolding,
            separator=self.splitter_additional_chars,
            maxlen=self.splitter_max_length,
        )
        parsed_query = node_splitter(parsed_query, splitter)

        # build an instance for the search
        resultsets = []
        for field in search_fields:
            sr = SearchRequest(self,
                               query=query,
                               parsetree=parsed_query,
                               field=field,
                               autoexpand=autoexpand,
                               similarity_ratio=similarity_ratio,
                               thesaurus=thesaurus,
                               language=language)

            # call the evaluator and produce a ResultSet instance
            resultsets.append(Evaluator(sr).run())
        resultset = unionResultSets(resultsets)

        # optional ranking using the cosine measure or another configure
        # ranking method
        if ranking:
            ranking_method = getUtility(IRanking, name=self.ranking_method)
            resultset.ranking(ranking_method,
                              index=self,
                              language=language,
                              nbest=ranking_maxhits)

        return resultset

    ############################################################
    # index attributes defined as properties
    ############################################################

    def _setUse_stemmer(self, value):
        if not value in (True, False, 0, 1):
            raise ValueError('"use_stemmer" must be either True or False')
        self._use_stemmer = bool(value)

    def _getUse_stemmer(self):
        return self._use_stemmer

    use_stemmer = property(_getUse_stemmer, _setUse_stemmer)

    def _setSplitter(self, value):
        self._splitter = value

    def _getSplitter(self):
        return self._splitter

    splitter = property(_getSplitter, _setSplitter)

    def _setLexicon(self, value):
        self.__lexicon = value  # using __lexicon instead of __lexicon to avoid a name clash                                   # (_lexicon is the lexicon object)

    def _getLexicon(self):
        return self.__lexicon

    lexicon = property(_getLexicon, _setLexicon)

    def _setDedicated_storage(self, value):
        if not value in (True, False):
            raise ValueError('"dedicated_storage" must be True or False')
        self._dedicated_storage = value

    def _getDedicated_storage(self):
        return self._dedicated_storage

    dedicated_storage = property(_getDedicated_storage, _setDedicated_storage)

    def _setSplitter_max_length(self, value):
        self._splitter_max_length = value

    def _getSplitter_max_length(self):
        return self._splitter_max_length

    splitter_max_length = property(_getSplitter_max_length,
                                   _setSplitter_max_length)

    def _setFields(self, value):
        self._fields = value

    def _getFields(self):
        return self._fields

    fields = property(_getFields, _setFields)

    def _setUse_normalizer(self, value):

        if not value in (True, False):
            raise ValueError('"use_normalizer" must be True or False')
        self._use_normalizer = value

    def _getUse_normalizer(self):
        return self._use_normalizer

    use_normalizer = property(_getUse_normalizer, _setUse_normalizer)

    def _setstorage(self, value):
        self.__storage = value

    def _getstorage(self):
        return self.__storage

    storage = property(_getstorage, _setstorage)

    def _setDefault_encoding(self, value):
        self._default_encoding = value

    def _getDefault_encoding(self):
        return self._default_encoding

    default_encoding = property(_getDefault_encoding, _setDefault_encoding)

    def _setLanguages(self, value):

        if not isinstance(value, (list, tuple)):
            raise ValueError(
                '"languages" must be list or tuple of country codes')
        if not value:
            raise ValueError('No languages given')

        self._languages = value

    def _getLanguages(self):
        return self._languages

    languages = property(_getLanguages, _setLanguages)

    def _setSplitter_additional_chars(self, value):
        self._splitter_additional_chars = value

    def _getSplitter_additional_chars(self):
        value = getattr(self, '_splitter_additional_chars', None)
        if value is None:
            return self._splitter_separators
        return value

    splitter_additional_chars = property(_getSplitter_additional_chars,
                                         _setSplitter_additional_chars)

    def _setQuery_parser(self, value):
        self._query_parser = value

    def _getQuery_parser(self):
        return self._query_parser

    query_parser = property(_getQuery_parser, _setQuery_parser)

    def _setSplitter_casefolding(self, value):

        if not value in (True, False):
            raise ValueError('"splitter_casefolding" must be True or False')

        self._splitter_casefolding = value

    def _getSplitter_casefolding(self):
        return self._splitter_casefolding

    splitter_casefolding = property(_getSplitter_casefolding,
                                    _setSplitter_casefolding)

    def _setIndex_unknown_languages(self, value):
        self._index_unknown_languages = value

    def _getIndex_unknown_languages(self):
        return self._index_unknown_languages

    index_unknown_languages = property(_getIndex_unknown_languages,
                                       _setIndex_unknown_languages)

    def _setAutoexpand(self, value):
        self._autoexpand = value

    def _getAutoexpand(self):
        return getattr(self, '_autoexpand', 'off')

    autoexpand = property(_getAutoexpand, _setAutoexpand)

    def _setAutoexpand_limit(self, value):
        self._autoexpand_limit = value

    def _getAutoexpand_limit(self):
        return self._autoexpand_limit

    autoexpand_limit = property(_getAutoexpand_limit, _setAutoexpand_limit)

    def _setRanking(self, value):
        self._ranking = value

    def _getRanking(self):
        return self._ranking

    ranking = property(_getRanking, _setRanking)

    def _setUse_stopwords(self, value):

        if not value in (True, False):
            raise ValueError('"use_stopwords" must be True or False')

        self._use_stopwords = value

    def _getUse_stopwords(self):
        return self._use_stopwords

    use_stopwords = property(_getUse_stopwords, _setUse_stopwords)

    ############################################################
    # Some helper methods
    ############################################################

    def _dump(self):
        """ perform low-level dump of the index """

        print 'Lexicon'
        for lang in self.getLexicon().getLanguages():
            print lang
            for k, v in self.getLexicon()._words[lang].items():
                print repr(k), v

            print

        print '-' * 80

        print 'Storage'
        for field in self.fields:
            S = self.getStorage(field)

            for k, v in S._wid2doc.items():
                print k, list(v)

    def __repr__(self):
        return '%s[%s]' % (self.__class__.__name__, ', '.join([
            '%s=%s' % (k, repr(getattr(self, k, None)))
            for k in defaults.keys()
        ]))

    def __len__(self):
        if self.dedicated_storage:
            return sum([len(s) for s in self._storage.values()])
        else:
            return len(self._storage)
Ejemplo n.º 34
0
class TopicIndex(Persistent, SimpleItem):
    """A TopicIndex maintains a set of FilteredSet objects.

    Every FilteredSet object consists of an expression and and IISet with all
    Ids of indexed objects that eval with this expression to 1.
    """

    meta_type = 'TopicIndex'
    zmi_icon = 'fas fa-info-circle'
    operators = ('or', 'and')
    useOperator = 'or'
    query_options = ('query', 'operator')

    manage_options = (
        {'label': 'FilteredSets', 'action': 'manage_main'},
    )

    def __init__(self, id, caller=None):
        self.id = id
        self.filteredSets = OOBTree()

    def getId(self):
        return self.id

    def clear(self):
        for fs in self.filteredSets.values():
            fs.clear()

    def index_object(self, docid, obj, threshold=100):
        """ hook for (Z)Catalog """
        for fid, filteredSet in self.filteredSets.items():
            filteredSet.index_object(docid, obj)
        return 1

    def unindex_object(self, docid):
        """ hook for (Z)Catalog """
        for fs in self.filteredSets.values():
            try:
                fs.unindex_object(docid)
            except KeyError:
                LOG.debug('Attempt to unindex document'
                          ' with id %s failed', docid)
        return 1

    def numObjects(self):
        """Return the number of indexed objects."""
        setlist = []
        for fs in self.filteredSets.values():
            setlist.append(fs.getIds())
        return len(multiunion(setlist))

    def indexSize(self):
        """Return the size of the index in terms of distinct values."""
        return len(self.filteredSets)

    def search(self, filter_id):
        f = self.filteredSets.get(filter_id, None)
        if f is not None:
            return f.getIds()

    def _apply_index(self, request):
        record = IndexQuery(request, self.id, self.query_options,
                            self.operators, self.useOperator)
        if record.keys is None:
            return None
        return (self.query_index(record), (self.id, ))

    def query_index(self, record, resultset=None):
        """Hook for (Z)Catalog
        'record' --  mapping type (usually {"topic": "..." }
        """
        operator = record.operator
        if operator == 'or':
            set_func = union
        else:
            set_func = intersection

        res = None
        for filter_id in record.keys:
            rows = self.search(filter_id)
            res = set_func(res, rows)

        if res:
            return res
        return IITreeSet()

    def hasUniqueValuesFor(self, name):
        """has unique values for column name"""
        if name == self.id:
            return 1
        return 0

    def uniqueValues(self, name=None, withLength=0):
        """Return an iterable/sequence of unique values for name.

        If 'withLengths' is true, returns a iterable/sequence of tuples of
        (value, length).
        """

        if name is None:
            name = self.id
        elif name != self.id:
            return

        if not withLength:
            for key in self.filteredSets.keys():
                yield key
        else:
            for key, value in self.filteredSets.items():
                yield (key, len(value.getIds()))

    def getEntryForObject(self, docid, default=None):
        """ Takes a document ID and returns all the information we have
            on that specific object.
        """
        res = []
        for fs in self.filteredSets.values():
            ids = fs.getIds()
            if docid in ids:
                res.append(fs.getId())

        return res or default

    def addFilteredSet(self, filter_id, typeFilteredSet, expr):
        # Add a FilteredSet object.
        if filter_id in self.filteredSets:
            raise KeyError(('A FilteredSet with this name already '
                            'exists: {0}'.format(filter_id)))
        self.filteredSets[filter_id] = factory(
            filter_id, typeFilteredSet, expr)

    def delFilteredSet(self, filter_id):
        # Delete the FilteredSet object specified by 'filter_id'.
        if filter_id not in self.filteredSets:
            raise KeyError(
                'no such FilteredSet:  {0}'.format(filter_id))
        del self.filteredSets[filter_id]

    def clearFilteredSet(self, filter_id):
        # Clear the FilteredSet object specified by 'filter_id'.
        f = self.filteredSets.get(filter_id, None)
        if f is None:
            raise KeyError('no such FilteredSet: {0}'.format(filter_id))
        f.clear()

    def manage_addFilteredSet(self, filter_id, typeFilteredSet, expr, URL1,
                              REQUEST=None, RESPONSE=None):
        """ add a new filtered set """

        if len(filter_id) == 0:
            raise RuntimeError('Length of ID too short')
        if len(expr) == 0:
            raise RuntimeError('Length of expression too short')

        self.addFilteredSet(filter_id, typeFilteredSet, expr)

        if RESPONSE:
            RESPONSE.redirect(URL1 + (
                '/manage_workspace?'
                'manage_tabs_message=FilteredSet%20added'))

    def manage_delFilteredSet(self, filter_ids=[], URL1=None,
                              REQUEST=None, RESPONSE=None):
        """ delete a list of FilteredSets"""

        for filter_id in filter_ids:
            self.delFilteredSet(filter_id)

        if RESPONSE:
            RESPONSE.redirect(URL1 + (
                '/manage_workspace?'
                'manage_tabs_message=FilteredSet(s)%20deleted'))

    def manage_saveFilteredSet(self, filter_id, expr, URL1=None,
                               REQUEST=None, RESPONSE=None):
        """ save expression for a FilteredSet """

        self.filteredSets[filter_id].setExpression(expr)

        if RESPONSE:
            RESPONSE.redirect(URL1 + (
                '/manage_workspace?'
                'manage_tabs_message=FilteredSet(s)%20updated'))

    def getIndexSourceNames(self):
        """ return names of indexed attributes """
        return ('n/a', )

    def getIndexQueryNames(self):
        return (self.id,)

    def manage_clearFilteredSet(self, filter_ids=[], URL1=None,
                                REQUEST=None, RESPONSE=None):
        """  clear a list of FilteredSets"""

        for filter_id in filter_ids:
            self.clearFilteredSet(filter_id)

        if RESPONSE:
            RESPONSE.redirect(URL1 + (
                '/manage_workspace?'
                'manage_tabs_message=FilteredSet(s)%20cleared'))

    manage = manage_main = DTMLFile('dtml/manageTopicIndex', globals())
    manage_main._setName('manage_main')
    editFilteredSet = DTMLFile('dtml/editFilteredSet', globals())
Ejemplo n.º 35
0
class BTreeFolder2Base(Persistent):
    """Base for BTree-based folders.
    """

    security = ClassSecurityInfo()

    manage_options=(
        ({'label': 'Contents', 'action': 'manage_main'},
         ) + Folder.manage_options[1:]
        )

    security.declareProtected(view_management_screens, 'manage_main')
    manage_main = DTMLFile('contents', globals())

    _tree = None      # OOBTree: { id -> object }
    _count = None     # A BTrees.Length
    _v_nextid = 0     # The integer component of the next generated ID
    _mt_index = None  # OOBTree: { meta_type -> OIBTree: { id -> 1 } }
    title = ''

    # superValues() looks for the _objects attribute, but the implementation
    # would be inefficient, so superValues() support is disabled.
    _objects = ()

    def __init__(self, id=None):
        if id is not None:
            self.id = id
        self._initBTrees()

    def _initBTrees(self):
        self._tree = OOBTree()
        self._count = Length()
        self._mt_index = OOBTree()

    def _populateFromFolder(self, source):
        """Fill this folder with the contents of another folder.
        """
        for name in source.objectIds():
            value = source._getOb(name, None)
            if value is not None:
                self._setOb(name, aq_base(value))

    security.declareProtected(view_management_screens, 'manage_fixCount')
    def manage_fixCount(self):
        """Calls self._fixCount() and reports the result as text.
        """
        old, new = self._fixCount()
        path = '/'.join(self.getPhysicalPath())
        if old == new:
            return "No count mismatch detected in BTreeFolder2 at %s." % path
        else:
            return ("Fixed count mismatch in BTreeFolder2 at %s. "
                    "Count was %d; corrected to %d" % (path, old, new))

    def _fixCount(self):
        """Checks if the value of self._count disagrees with
        len(self.objectIds()). If so, corrects self._count. Returns the
        old and new count values. If old==new, no correction was
        performed.
        """
        old = self._count()
        new = len(self.objectIds())
        if old != new:
            self._count.set(new)
        return old, new

    security.declareProtected(view_management_screens, 'manage_cleanup')
    def manage_cleanup(self):
        """Calls self._cleanup() and reports the result as text.
        """
        v = self._cleanup()
        path = '/'.join(self.getPhysicalPath())
        if v:
            return "No damage detected in BTreeFolder2 at %s." % path
        else:
            return ("Fixed BTreeFolder2 at %s.  "
                    "See the log for more details." % path)

    def _cleanup(self):
        """Cleans up errors in the BTrees.

        Certain ZODB bugs have caused BTrees to become slightly insane.
        Fortunately, there is a way to clean up damaged BTrees that
        always seems to work: make a new BTree containing the items()
        of the old one.

        Returns 1 if no damage was detected, or 0 if damage was
        detected and fixed.
        """
        from BTrees.check import check
        path = '/'.join(self.getPhysicalPath())
        try:
            check(self._tree)
            for key in self._tree.keys():
                if key not in self._tree:
                    raise AssertionError(
                        "Missing value for key: %s" % repr(key))
            check(self._mt_index)
            for key, value in self._mt_index.items():
                if (key not in self._mt_index
                    or self._mt_index[key] is not value):
                    raise AssertionError(
                        "Missing or incorrect meta_type index: %s"
                        % repr(key))
                check(value)
                for k in value.keys():
                    if k not in value:
                        raise AssertionError(
                            "Missing values for meta_type index: %s"
                            % repr(key))
            return 1
        except AssertionError:
            LOG.warn('Detected damage to %s. Fixing now.' % path,
                     exc_info=sys.exc_info())
            try:
                self._tree = OOBTree(self._tree)
                mt_index = OOBTree()
                for key, value in self._mt_index.items():
                    mt_index[key] = OIBTree(value)
                self._mt_index = mt_index
            except:
                LOG.error('Failed to fix %s.' % path,
                    exc_info=sys.exc_info())
                raise
            else:
                LOG.info('Fixed %s.' % path)
            return 0

    def _getOb(self, id, default=_marker):
        """Return the named object from the folder.
        """
        tree = self._tree
        if default is _marker:
            ob = tree[id]
            return ob.__of__(self)
        else:
            ob = tree.get(id, _marker)
            if ob is _marker:
                return default
            else:
                return ob.__of__(self)

    security.declareProtected(access_contents_information, 'get')
    def get(self, name, default=None):
        return self._getOb(name, default)

    def __getitem__(self, name):
        return self._getOb(name)

    def __getattr__(self, name):
        # Boo hoo hoo!  Zope 2 prefers implicit acquisition over traversal
        # to subitems, and __bobo_traverse__ hooks don't work with
        # restrictedTraverse() unless __getattr__() is also present.
        # Oh well.
        res = self._tree.get(name)
        if res is None:
            raise AttributeError(name)
        return res

    def _setOb(self, id, object):
        """Store the named object in the folder.
        """
        tree = self._tree
        if id in tree:
            raise KeyError('There is already an item named "%s".' % id)
        tree[id] = object
        self._count.change(1)
        # Update the meta type index.
        mti = self._mt_index
        meta_type = getattr(object, 'meta_type', None)
        if meta_type is not None:
            ids = mti.get(meta_type, None)
            if ids is None:
                ids = OIBTree()
                mti[meta_type] = ids
            ids[id] = 1

    def _delOb(self, id):
        """Remove the named object from the folder.
        """
        tree = self._tree
        meta_type = getattr(tree[id], 'meta_type', None)
        del tree[id]
        self._count.change(-1)
        # Update the meta type index.
        if meta_type is not None:
            mti = self._mt_index
            ids = mti.get(meta_type, None)
            if ids is not None and id in ids:
                del ids[id]
                if not ids:
                    # Removed the last object of this meta_type.
                    # Prune the index.
                    del mti[meta_type]

    security.declareProtected(view_management_screens, 'getBatchObjectListing')
    def getBatchObjectListing(self, REQUEST=None):
        """Return a structure for a page template to show the list of objects.
        """
        if REQUEST is None:
            REQUEST = {}
        pref_rows = int(REQUEST.get('dtpref_rows', 20))
        b_start = int(REQUEST.get('b_start', 1))
        b_count = int(REQUEST.get('b_count', 1000))
        b_end = b_start + b_count - 1
        url = self.absolute_url() + '/manage_main'
        idlist = self.objectIds()  # Pre-sorted.
        count = self.objectCount()

        if b_end < count:
            next_url = url + '?b_start=%d' % (b_start + b_count)
        else:
            b_end = count
            next_url = ''

        if b_start > 1:
            prev_url = url + '?b_start=%d' % max(b_start - b_count, 1)
        else:
            prev_url = ''

        formatted = []
        formatted.append(listtext0 % pref_rows)
        for i in range(b_start - 1, b_end):
            optID = escape(idlist[i])
            formatted.append(listtext1 % (escape(optID, quote=1), optID))
        formatted.append(listtext2)
        return {'b_start': b_start, 'b_end': b_end,
                'prev_batch_url': prev_url,
                'next_batch_url': next_url,
                'formatted_list': ''.join(formatted)}

    security.declareProtected(view_management_screens,
                              'manage_object_workspace')
    def manage_object_workspace(self, ids=(), REQUEST=None):
        '''Redirects to the workspace of the first object in
        the list.'''
        if ids and REQUEST is not None:
            REQUEST.RESPONSE.redirect(
                '%s/%s/manage_workspace' % (
                self.absolute_url(), quote(ids[0])))
        else:
            return self.manage_main(self, REQUEST)

    security.declareProtected(access_contents_information, 'tpValues')
    def tpValues(self):
        """Ensures the items don't show up in the left pane.
        """
        return ()

    security.declareProtected(access_contents_information, 'objectCount')
    def objectCount(self):
        """Returns the number of items in the folder."""
        return self._count()

    def __len__(self):
        return self.objectCount()

    def __nonzero__(self):
        return True

    security.declareProtected(access_contents_information, 'has_key')
    def has_key(self, id):
        """Indicates whether the folder has an item by ID.
        """
        return id in self._tree

    # backward compatibility
    hasObject = has_key

    security.declareProtected(access_contents_information, 'objectIds')
    def objectIds(self, spec=None):
        # Returns a list of subobject ids of the current object.
        # If 'spec' is specified, returns objects whose meta_type
        # matches 'spec'.

        if spec is None:
            return self.keys()

        if isinstance(spec, str):
            spec = [spec]

        set = None
        mti = self._mt_index
        for meta_type in spec:
            ids = mti.get(meta_type, None)
            if ids is not None:
                set = union(set, ids)
        if set is None:
            return ()
        else:
            return set.keys()

    security.declareProtected(access_contents_information, 'keys')
    def keys(self):
        return self._tree.keys()

    def __contains__(self, name):
        return name in self._tree

    def __iter__(self):
        return iter(self.objectIds())

    security.declareProtected(access_contents_information, 'objectValues')
    def objectValues(self, spec=None):
        # Returns a list of actual subobjects of the current object.
        # If 'spec' is specified, returns only objects whose meta_type
        # match 'spec'.
        if spec is None:
            return self.values()
        return LazyMap(self._getOb, self.objectIds(spec))

    security.declareProtected(access_contents_information, 'values')
    def values(self):
        return self._tree.values()

    security.declareProtected(access_contents_information, 'objectItems')
    def objectItems(self, spec=None):
        # Returns a list of (id, subobject) tuples of the current object.
        # If 'spec' is specified, returns only objects whose meta_type match
        # 'spec'
        if spec is None:
            return self.items()
        return LazyMap(lambda id, _getOb=self._getOb: (id, _getOb(id)),
                       self.objectIds(spec))

    security.declareProtected(access_contents_information, 'items')
    def items(self):
        return self._tree.items()

    security.declareProtected(access_contents_information, 'objectMap')
    def objectMap(self):
        # Returns a tuple of mappings containing subobject meta-data.
        return LazyMap(lambda (k, v):
                       {'id': k, 'meta_type': getattr(v, 'meta_type', None)},
                       self._tree.items(), self._count())

    security.declareProtected(access_contents_information, 'objectIds_d')
    def objectIds_d(self, t=None):
        ids = self.objectIds(t)
        res = {}
        for id in ids:
            res[id] = 1
        return res

    security.declareProtected(access_contents_information, 'objectMap_d')
    def objectMap_d(self, t=None):
        return self.objectMap()

    def _checkId(self, id, allow_dup=0):
        if not allow_dup and id in self:
            raise BadRequestException('The id "%s" is invalid--'
                                      'it is already in use.' % id)

    def _setObject(self, id, object, roles=None, user=None, set_owner=1,
                   suppress_events=False):
        ob = object # better name, keep original function signature
        v = self._checkId(id)
        if v is not None:
            id = v

        # If an object by the given id already exists, remove it.
        if id in self:
            self._delObject(id)

        if not suppress_events:
            notify(ObjectWillBeAddedEvent(ob, self, id))

        self._setOb(id, ob)
        ob = self._getOb(id)

        if set_owner:
            # TODO: eventify manage_fixupOwnershipAfterAdd
            # This will be called for a copy/clone, or a normal _setObject.
            ob.manage_fixupOwnershipAfterAdd()

            # Try to give user the local role "Owner", but only if
            # no local roles have been set on the object yet.
            if getattr(ob, '__ac_local_roles__', _marker) is None:
                user = getSecurityManager().getUser()
                if user is not None:
                    userid = user.getId()
                    if userid is not None:
                        ob.manage_setLocalRoles(userid, ['Owner'])

        if not suppress_events:
            notify(ObjectAddedEvent(ob, self, id))
            notifyContainerModified(self)

        compatibilityCall('manage_afterAdd', ob, ob, self)

        return id

    def __setitem__(self, key, value):
        return self._setObject(key, value)

    def _delObject(self, id, dp=1, suppress_events=False):
        ob = self._getOb(id)

        compatibilityCall('manage_beforeDelete', ob, ob, self)

        if not suppress_events:
            notify(ObjectWillBeRemovedEvent(ob, self, id))

        self._delOb(id)

        if not suppress_events:
            notify(ObjectRemovedEvent(ob, self, id))
            notifyContainerModified(self)

    def __delitem__(self, name):
        return self._delObject(id=name)

    # Utility for generating unique IDs.

    security.declareProtected(access_contents_information, 'generateId')
    def generateId(self, prefix='item', suffix='', rand_ceiling=999999999):
        """Returns an ID not used yet by this folder.

        The ID is unlikely to collide with other threads and clients.
        The IDs are sequential to optimize access to objects
        that are likely to have some relation.
        """
        tree = self._tree
        n = self._v_nextid
        attempt = 0
        while 1:
            if n % 4000 != 0 and n <= rand_ceiling:
                id = '%s%d%s' % (prefix, n, suffix)
                if id not in tree:
                    break
            n = randint(1, rand_ceiling)
            attempt = attempt + 1
            if attempt > MAX_UNIQUEID_ATTEMPTS:
                # Prevent denial of service
                raise ExhaustedUniqueIdsError
        self._v_nextid = n + 1
        return id
Ejemplo n.º 36
0
class UserRatingStorage(Contained, Persistent):
    """BTree-based storage for user ratings, keeps a running
    statistics tally for efficiency."""
    implements(IUserRating, IRatingStorage)

    _average = 0.0
    _anon_average = 0.0
    _most_recent = None
    _penultimate = None
    _length = None
    _anon_length = None
    # BBB
    scale = 5

    def __init__(self):
        """Setup our data structures"""
        self._anon_ratings = IOBTree()
        self._ratings = OOBTree()
        self._sessions = OOBTree()
        self._length = Length()
        self._anon_length = Length()

    def rate(self, rating, username=None, session_key=None):
        """Set a rating for a particular user"""
        # We keep a running average for efficiency, we need to
        # have the current statistics to do so
        orig_total = self._average * self.numberOfRatings
        orig_rating = self._ratings.get(username, 0.0)
        rating = Rating(rating, username)
        if username:
            self._ratings[username] = rating
            if not orig_rating:
                # If the user hadn't set a rating yet, the number of
                # ratings grew
                self._length.change(1)
        else:
            # For anonymous users, we use a sequential key, which
            # may lead to conflicts.  There's probably a better way
            anon_total = self._anon_average * self._anon_count
            # Update the corresponding BTree length to get the key
            self._anon_length.change(1)
            self._anon_ratings[self._anon_count] = rating
            self._anon_average = (anon_total + rating)/self._anon_count
            # If a session key was passed in for an anonymous user
            # store it with a datestamp
            if session_key:
                self._sessions[session_key] = datetime.utcnow()
        # Calculate the updated average
        self._average = (orig_total + rating - orig_rating)/self.numberOfRatings

        # If this isn't just a change in the last rating update the
        # most recent rating
        if self._most_recent and username != self._most_recent.userid:
            self._penultimate = self._most_recent

        # Mark this new rating as the most recent
        self._most_recent = rating

        return rating

    def userRating(self, username=None):
        """Retreive the rating for the specified user, or the average
        anonymous rating if no user is specified"""
        if username is not None:
            return self._ratings.get(username, None)
        else:
            if self._anon_count:
                return Rating(self._anon_average)
            else:
                return None

    def remove_rating(self, username):
        """Remove the rating for a given user"""
        orig_total = self._average * self.numberOfRatings
        rating = self._ratings[username]
        del self._ratings[username]
        self._length.change(-1)
        # Since we want to keep track of the most recent rating, we
        # need to replace it with the second most recent if the most
        # recent was deleted
        if rating is self.most_recent:
            self._most_recent = self._penultimate
            self._penultimate = None
            if self._most_recent is None:
                ordered = sorted(self.all_user_ratings(True),
                                 key=lambda x: x.timestamp)
                if ordered:
                    self._most_recent = ordered[-1]
                    if len(ordered > 1):
                        self._penultimate = ordered[-2]
                    else:
                        self._penultimate = None
                else:
                    self._most_recent = None
                    self._penultimate = None
        # Update the average
        self._average = float(self.numberOfRatings and
                                 (orig_total - rating)/self.numberOfRatings)

    @property
    def _anon_count(self):
        # Dynamic Migration
        if self._anon_length is None:
            self._anon_length = Length(len(self._anon_ratings))
        return self._anon_length()

    @property
    def most_recent(self):
        return self._most_recent

    def all_user_ratings(self, include_anon=False):
        ratings = self._ratings.values()
        if include_anon:
            ratings = chain(ratings, self._anon_ratings.values())
        return ratings

    def all_raters(self):
        return self._ratings.keys()

    @property
    def numberOfRatings(self):
        # Dynamic Migration
        if self._length is None:
            self._length = Length(len(self._ratings))
        return self._length() + self._anon_count

    @property
    def averageRating(self):
        return self._average

    def last_anon_rating(self, session_key):
        """Returns a timestamp indicating the last time the anonymous user
        with the given session_key rated the object."""
        return self._sessions.get(session_key, None)
class ToManyContRelationship(ToManyRelationshipBase):
    """
    ToManyContRelationship is the ToMany side of a realtionship that
    contains its related objects (like the normal Zope ObjectManager)
    """

    meta_type = "ToManyContRelationship"

    security = ClassSecurityInfo()

    def __init__(self, id):
        """set our instance values"""
        self.id = id
        self._objects = OOBTree()

    def _safeOfObjects(self):
        """
        Try to safely return ZenPack objects rather than
        causing imports to fail.
        """
        objs = []
        for ob in self._objects.values():
            try:
                objs.append(ob.__of__(self))
            except AttributeError:
                log.info("Ignoring unresolvable object '%s'", str(ob))
        return objs

    def __call__(self):
        """when we are called return our related object in our aq context"""
        return self._safeOfObjects()

    def __getattr__(self, name):
        """look in the two object stores for related objects"""
        if '_objects' in self.__dict__:
            objects = self._objects
            if objects.has_key(name): return objects[name]
        raise AttributeError("Unable to find the attribute '%s'" % name)

    def __hasattr__(self, name):
        """check to see if we have an object by an id
        this will fail if passed a short id and object is stored
        with fullid (ie: it is related not contained)
        use hasobject to get around this issue"""
        return self._objects.has_key(name)

    def hasobject(self, obj):
        "check to see if we have this object"
        return self._objects.get(obj.id) == obj

    def addRelation(self, obj):
        """Override base to run manage_afterAdd like ObjectManager"""
        if self._objects.has_key(obj.getId()):
            log.debug("obj %s already exists on %s", obj.getPrimaryId(),
                      self.getPrimaryId())

        notify(ObjectWillBeAddedEvent(obj, self, obj.getId()))
        ToManyRelationshipBase.addRelation(self, obj)
        obj = obj.__of__(self)
        o = self._getOb(obj.id)
        notify(ObjectAddedEvent(o, self, obj.getId()))

    def _setObject(self, id, object, roles=None, user=None, set_owner=1):
        """ObjectManager interface to add contained object."""
        unused(user, roles, set_owner)
        object.__primary_parent__ = aq_base(self)
        self.addRelation(object)
        return object.getId()

    def manage_afterAdd(self, item, container):
        # Don't do recursion anymore, a subscriber does that.
        pass

    manage_afterAdd.__five_method__ = True

    def manage_afterClone(self, item):
        # Don't do recursion anymore, a subscriber does that.
        pass

    manage_afterClone.__five_method__ = True

    def manage_beforeDelete(self, item, container):
        # Don't do recursion anymore, a subscriber does that.
        pass

    manage_beforeDelete.__five_method__ = True

    def _add(self, obj):
        """add an object to one side of a ToManyContRelationship.
        """
        id = obj.id
        if self._objects.has_key(id):
            raise RelationshipExistsError
        v = checkValidId(self, id)
        if v is not None: id = v
        self._objects[id] = aq_base(obj)
        obj = aq_base(obj).__of__(self)

    def _remove(self, obj=None, suppress_events=False):
        """remove object from our side of a relationship"""
        if obj: objs = [obj]
        else: objs = self.objectValuesAll()
        if not suppress_events:
            for robj in objs:
                notify(ObjectWillBeRemovedEvent(robj, self, robj.getId()))
        if obj:
            id = obj.id
            if not self._objects.has_key(id):
                raise ObjectNotFound("object %s not found on %s" %
                                     (obj.getPrimaryId(), self.getPrimaryId()))
            del self._objects[id]
        else:
            self._objects = OOBTree()
            self.__primary_parent__._p_changed = True
        if not suppress_events:
            for robj in objs:
                notify(ObjectRemovedEvent(robj, self, robj.getId()))

    def _remoteRemove(self, obj=None):
        """remove an object from the far side of this relationship
        if no object is passed in remove all objects"""
        if obj:
            if not self._objects.has_key(obj.id):
                raise ObjectNotFound("object %s not found on %s" %
                                     (obj.getPrimaryId(), self.getPrimaryId()))
            objs = [obj]
        else:
            objs = self.objectValuesAll()
        remoteName = self.remoteName()
        for obj in objs:
            rel = getattr(obj, remoteName)
            try:
                rel._remove(self.__primary_parent__)
            except ObjectNotFound:
                message = log_tb(sys.exc_info())
                log.error('Remote remove failed. Run "zenchkrels -r -x1". ' +
                          message)
                continue

    def _getOb(self, id, default=zenmarker):
        """look up in our local store and wrap in our aq_chain"""
        if self._objects.has_key(id):
            return self._objects[id].__of__(self)
        elif default == zenmarker:
            raise AttributeError("Unable to find %s" % id)
        return default

    security.declareProtected('View', 'objectIds')

    def objectIds(self, spec=None):
        """only return contained objects"""
        if spec:
            if isinstance(spec, basestring): spec = [spec]
            return [obj.id for obj in self._objects.values() \
                        if obj.meta_type in spec]
        return [k for k in self._objects.keys()]

    objectIdsAll = objectIds

    security.declareProtected('View', 'objectValues')

    def objectValues(self, spec=None):
        """override to only return owned objects for many to many rel"""
        if spec:
            if isinstance(spec, basestring): spec = [spec]
            return [ob.__of__(self) for ob in self._objects.values() \
                        if ob.meta_type in spec]
        return self._safeOfObjects()

    security.declareProtected('View', 'objectValuesAll')
    objectValuesAll = objectValues

    def objectValuesGen(self):
        """Generator that returns all related objects."""
        return (obj.__of__(self) for obj in self._objects.values())

    def objectItems(self, spec=None):
        """over ride to only return owned objects for many to many rel"""
        if spec:
            if isinstance(spec, basestring): spec = [spec]
            return [(key,value.__of__(self)) \
                for (key,value) in self._objects.items() \
                    if value.meta_type in spec]
        return [(key,value.__of__(self)) \
                    for (key,value) in self._objects.items()]

    objectItemsAll = objectItems

    #FIXME - need to make this work
    #    def all_meta_types(self, interfaces=None):
    #        mts = []
    #        for mt in ToManyRelationshipBase.all_meta_types(self, interfaces):
    #            if (mt.has_key('instance') and mt['instance']):
    #                for cl in self.sub_classes:
    #                    if checkClass(mt['instance'], cl):
    #                        mts.append(mt)
    #        return mts

    def _getCopy(self, container):
        """
        make new relation add copies of contained objs
        and refs if the relation is a many to many
        """
        rel = self.__class__(self.id)
        rel.__primary_parent__ = container
        rel = rel.__of__(container)
        norelcopy = getattr(self, 'zNoRelationshipCopy', [])
        if self.id in norelcopy: return rel
        for oobj in self.objectValuesAll():
            cobj = oobj._getCopy(rel)
            rel._setObject(cobj.id, cobj)
        return rel

    def checkValidId(self, id):
        """
        Is this a valid id for this container?
        """
        try:
            checkValidId(self, id)
        except:
            raise
        else:
            return True

    def exportXml(self, ofile, ignorerels=[]):
        """Return an xml representation of a ToManyContRelationship
        <tomanycont id='interfaces'>
            <object id='hme0'
                module='Products.Confmon.IpInterface' class='IpInterface'>
                <property></property> etc....
            </object>
        </tomanycont>
        """
        if self.countObjects() == 0: return
        ofile.write("<tomanycont id='%s'>\n" % self.id)
        for obj in self.objectValues():
            obj.exportXml(ofile, ignorerels)
        ofile.write("</tomanycont>\n")

    def checkRelation(self, repair=False):
        """Check to make sure that relationship bidirectionality is ok.
        """
        if len(self._objects):
            log.debug("checking relation: %s", self.id)
        else:
            return

        # look for objects that don't point back to us
        # or who should no longer exist in the database
        remoteName = self.remoteName()
        parentObject = self.getPrimaryParent()
        for obj in self._objects.values():
            if not hasattr(obj, remoteName):
                path = parentObject.getPrimaryUrlPath()
                if repair:
                    log.warn(
                        "Deleting %s object '%s' relation '%s' (missing remote relation '%s')",
                        path, obj, self.id, remoteName)
                    self._remove(obj, True)
                    continue
                else:
                    msg = "%s object '%s' relation '%s' missing remote relation '%s'" % (
                        path, obj, self.id, remoteName)
                    raise AttributeError(msg)

            rrel = getattr(obj, remoteName)
            if not rrel.hasobject(parentObject):
                log.error("remote relation %s doesn't point back to %s",
                          rrel.getPrimaryId(), self.getPrimaryId())
                if repair:
                    log.warn("reconnecting relation %s to relation %s",
                             rrel.getPrimaryId(), self.getPrimaryId())
                    rrel._add(parentObject)