Ejemplo n.º 1
0
    def delete(self, myId, convId, conv):
        log.debug("plugin:delete", convId)
        user_tuids = {}

        # Get the list of every user who responded to this event
        res = yield db.get_slice(convId, "eventResponses")
        attendees = [x.column.name.split(":", 1)[1] for x in res]

        # Add all the invited people of the item
        res = yield db.get_slice(convId, "items", ['invitees'])
        res = utils.supercolumnsToDict(res)
        attendees.extend(res["invitees"].keys())
        invitedPeople = res["invitees"].keys()

        log.debug("Maps", ["%s:%s"%(uId, convId) for \
                                       uId in attendees])

        # Get the Org and GroupIds if any.
        convMeta = conv["meta"]
        groupIds = convMeta["target"].split(",") if "target" in convMeta else []
        attendees.extend(groupIds+[convMeta["org"]])

        log.debug("Attendees", attendees)

        # Get the timeuuids that were inserted for this user
        res = yield db.multiget_slice(["%s:%s"%(uId, convId) for \
                                       uId in attendees], "userAgendaMap")
        res = utils.multiColumnsToDict(res)

        for k, v in res.iteritems():
            uid = k.split(":", 1)[0]
            tuids = v.keys()
            if tuids:
                user_tuids[uid] = tuids

        log.debug("userAgenda Removal", user_tuids)
        # Delete their entries in the user's list of event entries
        for attendee in user_tuids:
            yield db.batch_remove({'userAgenda': [attendee]},
                                    names=user_tuids[attendee])

        # Delete invitation entries for invited people
        invited_tuids = dict([[x, user_tuids[x]] for x in invitedPeople])
        log.debug("userAgenda Invitation Removal", invited_tuids)
        for attendee in invited_tuids:
            yield db.batch_remove({'userAgenda': ['%s:%s' %(attendee, 'I')]},
                                    names=invited_tuids[attendee])

        log.debug("eventResponses Removal", convId)
        # Delete the event's entry in eventResponses
        yield db.remove(convId, "eventResponses")

        log.debug("userAgendaMap Removal", user_tuids)
        # Delete their entries in userAgendaMap
        for attendee in user_tuids:
            yield db.batch_remove({'userAgendaMap': ["%s:%s"%(attendee, convId)]},
                                    names=user_tuids[attendee])
Ejemplo n.º 2
0
 def removeConvFromFeed(x):
     timestamps = [col.column.name for col in x]
     if timestamps:
         yield db.batch_remove({'feed': [feedId]}, names=timestamps)
     else:
         yield db.remove(feedId, 'feed', conv['meta']['uuid'])
     yield db.remove(feedId, 'feedItems', super_column=convId)
Ejemplo n.º 3
0
def _cleanupMissingComments(convId, missingIds, itemResponses):
    missingKeys = []
    for response in itemResponses:
        userKey, responseKey = response.column.value.split(':')
        if responseKey in missingIds:
            missingKeys.append(response.column.name)

    d1 = db.batch_remove({'itemResponses': [convId]}, names=missingKeys)
    d1.addCallback(lambda x: db.get_count(convId, "itemResponses"))
    d1.addCallback(lambda x: db.insert(convId, 'items', \
                             str(x), 'responseCount', 'meta'))
    return d1
Ejemplo n.º 4
0
    def _verifyProfile(self, request):
        email = utils.getRequestArg(request, 'email')
        token = utils.getRequestArg(request, 'token')

        if not (email and token):
            raise MissingParams(['Email', 'Account Verification Token'])

        cols = yield db.get_slice(email, "userAuth", ["reactivateToken", "isFlagged"])
        cols = utils.columnsToDict(cols)
        if "isFlagged" in cols:
            storedToken = cols.get("reactivateToken", None)
            if storedToken == token:
                yield db.batch_remove({"userAuth": [email]},
                                    names=["reactivateToken", "isFlagged"])

        request.redirect('/signin')
Ejemplo n.º 5
0
    def _editWorkInfo(self, request):
        # Contact information at work.
        myId = request.getSession(IAuthInfo).username
        orgId = request.getSession(IAuthInfo).organization

        me = base.Entity(myId)
        yield me.fetchData([])
        data = {}
        to_remove = []

        for field in ["phone", "mobile"]:
            val = utils.getRequestArg(request, field)
            if val:
                data[field] = val
            else:
                to_remove.append(field)

        if 'phone' in data and not re.match('^\+?[0-9x\- ]{5,20}$', data['phone']):
            raise errors.InvalidRequest(_('Phone numbers can only have numerals, hyphens, spaces and a plus sign'))

        if 'mobile' in data and not re.match('^\+?[0-9x\- ]{5,20}$', data['mobile']):
            raise errors.InvalidRequest(_('Phone numbers can only have numerals, hyphens, spaces and a plus sign'))

        if data:
            yield db.batch_insert(myId, "entities", {"contact": data})
        if to_remove:
            yield db.batch_remove({"entities":[myId]}, names=to_remove, supercolumn='contact')

        contactInfo = me.get('contact', {})
        if any([contactInfo.get(x, None) != data.get(x, None) for x in ["phone", "mobile"]]):
            request.write('$$.alerts.info("%s");' % _('Profile updated'))

        args = {"detail": "", "me": me}
        suggestedSections = yield self._checkProfileCompleteness(request, myId, args)
        tmp_suggested_sections = {}
        for section, items in suggestedSections.iteritems():
            if len(suggestedSections[section]) > 0:
                tmp_suggested_sections[section] = items
        args.update({'suggested_sections':tmp_suggested_sections})

        t.renderScriptBlock(request, "settings.mako", "right",
                            False, ".right-contents", "set", **args)
        me.update({'contact':data})
        yield search.solr.updatePeopleIndex(myId, me, orgId)
Ejemplo n.º 6
0
    def resetPassword(self, request):
        email = utils.getRequestArg(request, 'email')
        token = utils.getRequestArg(request, 'token')
        passwd = utils.getRequestArg(request, 'password', False)
        pwdrepeat = utils.getRequestArg(request, 'pwdrepeat', False)

        if not (email and token and passwd and pwdrepeat):
            raise MissingParams(['Email', 'Password Reset Token'])

        if (passwd != pwdrepeat):
            raise errors.PasswordsNoMatch()

        validEmail, tokens, deleteTokens, leastTimestamp = yield _getResetPasswordTokens(email)
        if validEmail:
            if token not in tokens:
                raise PermissionDenied("Invalid token. <a href='/password/resend?email=%s'>Click here</a> to reset password" % (email))
            yield db.insert(email, "userAuth", utils.hashpass(passwd), 'passwordHash')
            yield db.batch_remove({"userAuth": [email]}, names=deleteTokens)
        request.redirect('/signin')
Ejemplo n.º 7
0
 def cleaner(convIds):
     deleteKeys = []
     for key, value in itemsFromFeed.items():
         if value in deleted:
             deleteKeys.append(key)
     yield db.batch_remove({'tagItems': [tagId]}, names=deleteKeys)
Ejemplo n.º 8
0
    def _addUser(self, request):
        emailId = utils.getRequestArg(request, 'email')
        existingUser = db.get_count(emailId, "userAuth")

        localpart, domain = emailId.split("@")
        displayName = utils.getRequestArg(request, 'name')
        jobTitle = utils.getRequestArg(request, 'jobTitle')
        timezone = utils.getRequestArg(request, 'timezone')
        passwd = utils.getRequestArg(request, 'password', sanitize=False)
        pwdrepeat = utils.getRequestArg(request, 'pwdrepeat', sanitize=False)
        if not displayName or not jobTitle or not timezone or not passwd:
            raise errors.MissingParams([_("All fields are required to create the user")])

        if passwd != pwdrepeat:
            raise PasswordsNoMatch()

        args = {'emailId': emailId, 'view': 'invite'}

        existingUser = yield existingUser
        if not existingUser:
            authinfo = yield defer.maybeDeferred(request.getSession, IAuthInfo)
            orgId = yield getOrgId(domain)
            if not orgId:
                orgId = utils.getUniqueKey()
                domains = {domain: ''}
                basic = {"name": domain, "type": "org"}
                yield db.batch_insert(orgId, "entities", {"basic": basic,
                                                          "domains": domains})
                yield db.insert(domain, "domainOrgMap", '', orgId)

            userId = yield utils.addUser(emailId, displayName, passwd,
                                         orgId, jobTitle, timezone)
            authinfo.username = userId
            authinfo.organization = orgId
            authinfo.isAdmin = False
            yield request._saveSessionToDB()

            cols = yield db.get_slice(domain, "invitations", [emailId])
            cols = utils.supercolumnsToDict(cols)

            userIds = cols.get(emailId, {}).values()
            if userIds:
                db.batch_remove({'invitationsSent': userIds}, names=[emailId])

            yield db.remove(domain, "invitations", super_column=emailId)
            t.render(request, "signup.mako", **args)

            # Notify all invitees about this user.
            token = utils.getRequestArg(request, "token")
            acceptedInvitationSender = cols.get(emailId, {}).get(token)
            otherInvitees = [x for x in userIds
                             if x not in (acceptedInvitationSender, emailId)]

            entities = base.EntitySet(userIds + [orgId, userId])
            yield entities.fetchData()
            data = {"entities": entities, 'orgId': orgId}

            yield notifications.notify([acceptedInvitationSender], ":IA", userId, **data)
            yield notifications.notify(otherInvitees, ":NU", userId, **data)
        else:
            raise InvalidRegistration("A user with this e-mail already exists! Already registered?")
Ejemplo n.º 9
0
def get(auth, feedId=None, feedItemsId=None, convIds=None,
        getFn=None, cleanFn=None, start='', count=10, getReasons=True,
        forceCount=False, itemType=None):
    """Fetch data from feed represented by feedId. Returns a dictionary
    that has the items from feed, start of the next page and responses and
    likes that I know of.

    Keyword params:
    @auth -  An instance of AuthInfo representing the authenticated user
    @feedId -  Id of the feed from which the data is to be fetched
    @feedItemsId -  Id of the feed from with feed items must be fetched
    @convIds -  List of conversation id's to be used as feed
    @getFn -  Function that must be used to fetch items
    @cleanFn -  Function that must be used to clean items that don't exist
    @start -  Id of the item where the fetching must start
    @count - Number of items to fetch
    @getReasons - Add reason strings to the returned dictionary
    @forceCount - Try hard to get atleast count items from the feed

    """
    toFetchItems = set()    # Items and entities that need to be fetched
    toFetchEntities = set() #
    toFetchTags = set()     #

    items = {}              # Fetched items, entities and tags
    entities = base.EntitySet([])#
    tags = {}               #

    deleted = []            # List of items that were deleted
    deleteKeys = []         # List of keys that need to be deleted

    responses = {}          # Cached data of item responses and likes
    likes = {}              #
    myLikes = {}

    myId = auth.username
    orgId = auth.organization

    feedSource = "feed_%s" % itemType\
                        if itemType and itemType in plugins\
                                    and plugins[itemType].hasIndex\
                        else "feed"
    feedItemsId = feedItemsId or myId
    feedItems_d = []            # List of deferred used to fetch feedItems

    # Used for checking ACL
    relation = Relation(myId, [])
    yield relation.initGroupsList()

    # The updates that will be used to build reason strings
    convReasonUpdates = {}

    # Data that is sent to various plugins and returned by this function
    # XXX: myKey is depricated - use myId
    data = {"myId": myId, "orgId": orgId, "responses": responses,
            "likes": likes, "myLikes": myLikes, "items": items,
            "entities": entities, "tags": tags, "myKey": myId,
            "relations": relation}

    @defer.inlineCallbacks
    def fetchFeedItems(ids):
        rawFeedItems = yield db.get_slice(feedItemsId, "feedItems", ids) \
                                            if ids else defer.succeed([])
        for conv in rawFeedItems:
            convId = conv.super_column.name
            convUpdates = conv.super_column.columns
            responses[convId] = []
            likes[convId] = []
            latest = None

            updatesByType = {}
            for update in convUpdates:
                parts = update.value.split(':')
                updatesByType.setdefault(parts[0], []).append(parts)
                if parts[1] != myId:    # Ignore my own updates
                    latest = parts      # when displaying latest actors

            # Parse all notification to make sure we fetch any required
            # items, entities. and cache generic stuff that we display
            for tipe in updatesByType.keys():
                updates = updatesByType[tipe]

                if tipe in _feedUpdatePlugins:
                    (i,e) = _feedUpdatePlugins[tipe].parse(convId, updates)
                    toFetchItems.update(i)
                    toFetchEntities.update(e)

                if tipe == "L":
                    for update in updates:
                        if update[2] == convId:
                            likes[convId].append(update[1])
                        # XXX: Adding this item may break the sorting
                        #      of responses on this conversation
                        #      Bug #493
                        #else:
                        #    responses[convId].append(update[2])
                elif tipe in ["C", "Q"]:
                    for update in updates:
                        responses[convId].append(update[2])

            # Store any information that can be used to render
            # the reason strings when we have the required data
            if getReasons and latest:
                convReasonUpdates[convId] = updatesByType[latest[0]]

    # Fetch the feed if required and at the same time make sure
    # we delete unwanted items from the feed (cleanup).
    # NOTE: We assume that there will be very few deletes.
    nextPageStart = None
    if not convIds:
        feedId = feedId or myId
        allFetchedConvIds = set()   # Complete set of convIds fetched
        itemsFromFeed = {}          # All the key-values retrieved from feed
        keysFromFeed = []           # Sorted list of keys (used for paging)
        convIds = []                # List of convIds that will be displayed

        fetchStart = utils.decodeKey(start)
        fetchCount = count + 1

        while len(convIds) < count:
            fetchedConvIds = []

            # Use the getFn function if given.
            # NOTE: Part of this code is duplicated just below this.
            if getFn:
                results = yield getFn(start=fetchStart, count=fetchCount)
                for name, value in results.items():
                    keysFromFeed.append(name)
                    if value not in allFetchedConvIds:
                        fetchedConvIds.append(value)
                        allFetchedConvIds.add(value)
                        itemsFromFeed[name] = value
                    else:
                        deleteKeys.append(name)

            # Fetch user's feed when getFn isn't given.
            # NOTE: Part of this code is from above
            else:
                results = yield db.get_slice(feedId, feedSource,
                                             count=fetchCount,
                                             start=fetchStart, reverse=True)
                for col in results:
                    value = col.column.value
                    keysFromFeed.append(col.column.name)
                    if value not in allFetchedConvIds:
                        fetchedConvIds.append(value)
                        allFetchedConvIds.add(value)
                        itemsFromFeed[col.column.name] = value
                    else:
                        deleteKeys.append(col.column.name)

            # Initiate fetching feed items for all the conversation Ids.
            # Meanwhile we check if the authenticated user has access to
            # all the fetched conversation ids.
            # NOTE: Conversations would rarely be filtered out. So, we
            #       just go ahead with fetching data for all convs.
            feedItems_d.append(fetchFeedItems(fetchedConvIds))
            (filteredConvIds, deletedIds) = yield utils.fetchAndFilterConvs\
                                (fetchedConvIds, relation, items, myId, orgId)
            convIds.extend(filteredConvIds)
            deleted.extend(deletedIds)

            # Unless we are forced to fetch count number of items, we only
            # iterate till we fetch atleast half of them
            if (not forceCount and len(convIds) > (count/2)) or\
                    len(results) < fetchCount:
                break

            # If we need more items, we start fetching from where we
            # left in the previous iteration.
            fetchStart = keysFromFeed[-1]

        # If DB fetch got as many items as I requested
        # there may be additional items present in the feed
        # So, we cut one item from what we return and start the
        # next page from there.
        if len(results) == fetchCount:
            lastConvId = convIds[-1]
            for key in reversed(keysFromFeed):
                if key in itemsFromFeed and itemsFromFeed[key] == lastConvId:
                    nextPageStart = utils.encodeKey(key)
            convIds = convIds[:-1]

    else:
        (convIds, deletedIds) = yield utils.fetchAndFilterConvs(convIds,
                                            relation, items, myId, orgId)
        # NOTE: Unlike the above case where we fetch convIds from
        #       database (where we set the nextPageStart to a key),
        #       here we set nextPageStart to the convId.
        if len(convIds) > count:
            nextPageStart = utils.encodeKey(convIds[count])
            convIds = convIds[0:count]

        # Since convIds were directly passed to us, we would also
        # return the list of convIds deleted back to the caller.
        if deletedIds:
            data["deleted"] = deletedIds

    # We don't have any conversations to display!
    if not convIds:
        defer.returnValue({"conversations": []})

    # Delete any convs that were deleted from the feeds and
    # any duplicates that were marked for deletion
    cleanup_d = []
    if deleted:
        for key, value in itemsFromFeed.items():
            if value in deleted:
                deleteKeys.append(key)

        if cleanFn:
            d1 = cleanFn(list(deleteKeys))
        else:
            d1 = db.batch_remove({feedSource: [feedId]}, names=deleteKeys)

        d2 = db.batch_remove({'feedItems': [feedId]}, names=list(deleted))
        cleanup_d = [d1, d2]

    # We now have a filtered list of conversations that can be displayed
    # Let's wait till all the feed items have been fetched and processed
    yield defer.DeferredList(feedItems_d)

    # Fetch the remaining items (comments on the actual conversations)
    items_d = db.multiget_slice(toFetchItems, "items", ["meta" ,"attachments"])

    # Fetch tags on all the conversations that will be displayed
    for convId in convIds:
        conv = items[convId]
        toFetchEntities.add(conv["meta"]["owner"])
        if "target" in conv["meta"]:
            toFetchEntities.update(conv["meta"]["target"].split(','))
        toFetchTags.update(conv.get("tags",{}).keys())

    tags_d = db.get_slice(orgId, "orgTags", toFetchTags) \
                                if toFetchTags else defer.succeed([])

    # Fetch the list of my likes.
    # XXX: Latency can be pretty high here becuase many nodes will have to
    #      be contacted for the information.  Alternative could be to cache
    #      all likes by a user somewhere.
    myLikes_d = db.multiget(toFetchItems.union(convIds), "itemLikes", myId)

    # Fetch extra data that is required to render special items
    # We already fetched the conversation items, plugins merely
    # add more data to the already fetched items
    for convId in convIds[:]:
        itemType = items[convId]["meta"]["type"]
        if itemType in plugins:
            try:
                entityIds = yield plugins[itemType].fetchData(data, convId)
                toFetchEntities.update(entityIds)
            except Exception, e:
                log.err(e)
                convIds.remove(convId)
Ejemplo n.º 10
0
    def _getKeywordMatches(self, request, keyword, start='', count=10):
        args = {}
        authinfo = request.getSession(IAuthInfo)
        myId = authinfo.username
        orgId = authinfo.organization

        items = {}
        itemIds = []
        itemIdKeyMap = {}
        allFetchedItems = set()
        deleted = set()

        fetchStart = utils.decodeKey(start)
        fetchCount = count + 2
        while len(itemIds) < count:
            fetchedItemIds = []
            toFetchItems = set()

            results = yield db.get_slice(orgId + ":" + keyword, "keywordItems",
                                         count=fetchCount, start=fetchStart,
                                         reverse=True)
            for col in results:
                fetchStart = col.column.name
                itemAndParentIds = col.column.value.split(':')
                itemIdKeyMap[itemAndParentIds[0]] = fetchStart
                fetchedItemIds.append(itemAndParentIds[0])
                for itemId in itemAndParentIds:
                    if itemId not in allFetchedItems:
                        toFetchItems.add(itemId)
                        allFetchedItems.add(itemId)

            if toFetchItems:
                fetchedItems = yield db.multiget_slice(toFetchItems, "items",
                                                       ["meta", "attachments"])
                fetchedItems = utils.multiSuperColumnsToDict(fetchedItems)
                items.update(fetchedItems)

            for itemId in fetchedItemIds:
                item = items[itemId]
                if not 'meta' in item:
                    continue

                state = item['meta'].get('state', 'published')
                if state == 'deleted':
                    deleted.add(itemIdKeyMap[itemId])
                elif utils.checkAcl(myId, orgId, True, None, item['meta']):
                    itemIds.append(itemId)

            if len(results) < fetchCount:
                break

        if len(itemIds) > count:
            nextPageStart = utils.encodeKey(itemIdKeyMap[itemIds[-1]])
            itemIds = itemIds[:-1]
        else:
            nextPageStart = None

        dd = db.batch_remove({'keywordItems': [orgId + ':' + keyword]},
                             names=deleted) if deleted else defer.succeed([])

        args.update({'items': items, 'myId': myId})
        toFetchEntities = set()
        extraDataDeferreds = []
        for itemId in itemIds:
            item = items[itemId]
            itemMeta = item['meta']
            toFetchEntities.add(itemMeta['owner'])
            if 'target' in itemMeta:
                toFetchEntities.update(itemMeta['target'].split(','))
            if 'parent' in itemMeta:
                parentId = itemMeta['parent']
                if parentId in items:
                    toFetchEntities.add(items[parentId]['meta']['owner'])

            itemType = itemMeta.get('type', 'status')
            if itemType in plugins:
                d = plugins[itemType].fetchData(args, itemId)
                extraDataDeferreds.append(d)

        result = yield defer.DeferredList(extraDataDeferreds)
        for success, ret in result:
            if success:
                toFetchEntities.update(ret)

        fetchedEntities = {}
        if toFetchEntities:
            fetchedEntities = base.EntitySet(toFetchEntities)
            yield fetchedEntities.fetchData()

        yield dd
        args.update({'entities': fetchedEntities,
                     'matches': itemIds, 'nextPageStart': nextPageStart})
        defer.returnValue(args)
Ejemplo n.º 11
0
    def _submitReport(self, request, action):
        (appchange, script, args, myId) = yield self._getBasicArgs(request)
        landing = not self._ajax
        snippet, comment = utils.getTextWithSnippet(request, "comment",
                                            constants.COMMENT_PREVIEW_LENGTH)
        orgId = args['orgId']
        isNewReport = False
        timeUUID = uuid.uuid1().bytes

        convId, conv = yield utils.getValidItemId(request, "id")
        convMeta = conv["meta"]
        convOwnerId = convMeta["owner"]
        convType = convMeta["type"]
        convACL = convMeta["acl"]
        toFetchEntities = set([myId, convOwnerId])

        if "reportId" in convMeta:
            reportId = convMeta["reportId"]
            isNewReport = False
            toFetchEntities.add(convMeta['reportedBy'])
        else:
            isNewReport = True
            reportId = utils.getUniqueKey()

        if isNewReport and convOwnerId == myId:
            raise errors.InvalidRequest(_("You cannot report your own Item. \
                                                Delete the item instead"))

        toFetchEntities.remove(myId)
        entities = base.EntitySet(toFetchEntities)
        yield entities.fetchData()
        entities.update({myId: args["me"]})

        if myId == convOwnerId:
            if action not in ["accept", "repost"]:
                raise errors.InvalidRequest(_('Invalid action was performed on the report'))

            convReport = {"reportStatus": action}
            yield db.batch_insert(convId, "items", {"meta": convReport})

            if action == "accept":
                # Owner removed the comment. Delete the item from his feed
                yield Item.deleteItem(convId, myId, orgId)
                request.write("$$.fetchUri('/feed/');")
                request.write("$$.alerts.info('%s')" % _("Your item has been deleted"))
                request.finish()
            else:
                # Owner posted a reply, so notify reporter of the same
                yield Item._notify("RFC", convId, timeUUID, convType=convType,
                               convOwnerId=convOwnerId, myId=myId, entities=entities,
                               me=args["me"], reportedBy=convMeta["reportedBy"])
        else:
            if action not in ["report", "repost", "reject"]:
                raise errors.InvalidRequest(_('Invalid action was performed on the report'))

            if isNewReport:
                # Update Existing Item Information with Report Meta
                newACL = pickle.dumps({"accept": {"users": [convOwnerId, myId]}})
                convReport = {"reportedBy": myId, "reportId": reportId,
                              "reportStatus": "pending", "state": "flagged"}
                convMeta.update(convReport)
                yield db.batch_insert(convId, "items", {"meta": convReport})

                reportLink = """&#183;<a class="button-link" title="View Report" href="/item/report?id=%s"> View Report</a>""" % convId
                request.write("""$("#item-footer-%s").append('%s');""" % (convId, reportLink))
                yield Item._notify("FC", convId, timeUUID, convType=convType, entities=entities,
                              convOwnerId=convOwnerId, myId=myId, me=args["me"])
            else:
                if action == "repost":
                    # Remove the reportId key, so owner cannot post any comment
                    yield db.batch_remove({'items': [convId]},
                                            names=["reportId", "reportStatus",
                                                   "reportedBy", "state"],
                                            supercolumn='meta')

                    oldReportMeta = {"reportedBy": convMeta["reportedBy"],
                                     "reportId": reportId}

                    # Save the now resolved report in items and remove its
                    #  reference in the item meta so new reporters wont't see
                    #  old reports
                    timestamp = str(int(time.time()))
                    yield db.insert(convId, "items", reportId, timestamp, "reports")
                    yield db.batch_insert(reportId, "items", {"meta": oldReportMeta})

                    # Notify the owner that the report has been withdrawn
                    yield Item._notify("UFC", convId, timeUUID, convType=convType,
                                  convOwnerId=convOwnerId, myId=myId,
                                  entities=entities, me=args["me"])

                elif action  in ["reject", "report"]:
                    # Reporter rejects the comment by the owner or reports the
                    #  same item again.
                    convReport = {"reportStatus": "pending"}
                    yield Item._notify("RFC", convId, timeUUID, convType=convType,
                                  convOwnerId=convOwnerId, myId=myId, entities=entities,
                                  me=args["me"], reportedBy=convMeta["reportedBy"])
                    yield db.batch_insert(convId, "items", {"meta": convReport})

        args.update({"entities": entities, "ownerId": convOwnerId,
                     "convId": convId})

        # Update New Report comment Details
        commentId = utils.getUniqueKey()
        timeUUID = uuid.uuid1().bytes
        meta = {"owner": myId, "parent": reportId, "comment": comment,
                "timestamp": str(int(time.time())),
                "uuid": timeUUID, "richText": str(False)}
        if snippet:
            meta['snippet'] = snippet

        yield db.batch_insert(commentId, "items", {'meta': meta})

        # Update list of comments for this report
        yield db.insert(reportId, "itemResponses",
                        "%s:%s:%s" % (myId, commentId, action), timeUUID)

        yield self._renderReportResponses(request, convId, convMeta, args)
        request.write("$('#report-comment').attr('value', '')")
Ejemplo n.º 12
0
def updateFeedResponses(userKey, parentKey, itemKey, timeuuid, itemType,
                        responseType, convOwner, commentOwner, tagId,
                        entities, promote):
    if not entities:
        entities = [commentOwner]
    else:
        entities.extend([commentOwner])
    entities = ",".join(entities)

    feedItemValue = ":".join([responseType, commentOwner, itemKey, entities, tagId])
    tmp, oldest, latest = {}, None, None

    cols = yield db.get_slice(userKey, "feedItems",
                              super_column=parentKey, reverse=True)
    cols = utils.columnsToDict(cols, ordered=True)

    feedKeys = []
    userFeedItems = []
    userFeedItemsByType = {}
    for tuuid, val in cols.items():
        # Bailout if we already know about this update.
        if tuuid == timeuuid:
            defer.returnValue(None)

        rtype = val.split(':')[0]
        if rtype not in  ('!', 'I'):
            tmp.setdefault(rtype, []).append(tuuid)
            if val.split(':')[1] == userKey:
                userFeedItems.append(tuuid)
                userFeedItemsByType.setdefault(rtype, []).append(tuuid)
            oldest = tuuid

        feedKeys.append(tuuid)

    # Remove older entries of this conversation from the feed
    # only if a new one was added before this function was called.
    if promote and feedKeys:
        yield db.batch_remove({'feed': [userKey]}, names=feedKeys)

    totalItems = len(cols)
    noOfItems = len(tmp.get(responseType, []))

    if noOfItems == MAXFEEDITEMSBYTYPE:
        if (len(userFeedItemsByType.get(responseType, {})) == MAXFEEDITEMSBYTYPE  and not promote)\
           or (tmp[responseType][noOfItems-1] not in userFeedItemsByType.get(responseType, {}) \
               and len(userFeedItemsByType.get(responseType, {})) == MAXFEEDITEMSBYTYPE-1 and not promote):
             oldest = userFeedItemsByType[responseType][noOfItems-2]
        else:
            oldest = tmp[responseType][noOfItems-1]

    if ((len(userFeedItems)== MAXFEEDITEMS-1 and not promote) or \
       (oldest not in userFeedItems and len(userFeedItems) == MAXFEEDITEMS-2 and not promote)):

        oldest = userFeedItems[-2]

    if noOfItems == MAXFEEDITEMSBYTYPE or totalItems == MAXFEEDITEMS:
        yield db.remove(userKey, "feedItems", oldest, parentKey)
        if plugins.has_key(itemType) and plugins[itemType].hasIndex:
            yield db.remove(userKey, "feed_"+itemType, oldest)

    if totalItems == 0 and responseType != 'I':
        value = ":".join(["!", convOwner, parentKey, ""])
        tuuid = uuid.uuid1().bytes
        yield db.batch_insert(userKey, "feedItems", {parentKey:{tuuid:value}})

    yield db.batch_insert(userKey, "feedItems",
                          {parentKey:{timeuuid: feedItemValue}})
Ejemplo n.º 13
0
def migrateFriendsToFollowers():
    # Migrate all friends to followers/subscriptions.
    connectionRows = yield db.get_range_slice('connections', count=10000)
    for connectionRow in connectionRows:
        userId = connectionRow.key
        friends = [x.super_column.name for x in connectionRow.columns]
        yield db.batch_insert(userId, "followers", dict([(x, '') for x in friends]))
        yield db.batch_mutate(dict([(x, {'subscriptions': {userId: ''}}) for x in friends]))
    log.msg('>>>>>>>> Converted all connections to following.')

    # Remove name indices of friends
    entityRows = yield db.get_range_slice('entities', count=10000, names=['basic'])
    entities = dict([(x.key, utils.supercolumnsToDict(x.columns)) for x in entityRows])
    userIds = [x for x in entities.keys() if entities[x]['basic']['type'] == 'user']
    for userId in userIds:
        yield db.remove(userId, 'displayNameIndex')
        yield db.remove(userId, 'nameIndex')
    log.msg('>>>>>>>> Removed name indices for friends.')

    # Convert all "connection" activity to "follow".
    # We already have two separate items, so subtype conversion should be good.
    itemRows = yield db.get_range_slice('items', count=10000, names=['meta'])
    items = dict([(x.key, utils.supercolumnsToDict(x.columns)) for x in itemRows])
    connectionItems = [x for x in items.keys()\
                       if items[x]['meta'].get('type', '') == 'activity'\
                          and items[x]['meta']['subType'] == 'connection']
    yield db.batch_mutate(dict([(x, {'items':{'meta':{'subType':'following'}}}) for x in connectionItems]))
    log.msg('>>>>>>>> All connection items converted to following.')

    # Remove all friend requests from pendingConnections
    pendingRows = yield db.get_range_slice('pendingConnections', count=10000)
    for pendingRow in pendingRows:
        userId = pendingRow.key
        pendingFriendRequestIds = [x.column.name for x in pendingRow.columns \
                                   if not x.column.name.startswith('G')]
        if pendingFriendRequestIds:
            yield db.batch_remove({'pendingConnections': [userId]}, names=pendingFriendRequestIds)
    log.msg('>>>>>>>> Removed pending friend requests.')

    # Remove all friend requests from latest
    yield db.batch_remove({'latest': userIds}, names='people')
    log.msg('>>>>>>>> Removed friend requests from latest.')

    # Remove all friend-request-accepted notifications
    notifyMutations = {}
    for userId in userIds:
        items = yield db.get_slice(userId, "notificationItems", super_column=':FA')
        if items:
            names = [col.column.name for col in items]
            colmap = dict([(x, None) for x in names])
            deletion = Deletion(time.time() * 1000000, 'notifications',
                                SlicePredicate(column_names=names))
            notifyMutations[userId] = {'notifications': colmap, 'latest': [deletion]}
            yield db.remove(userId, 'notificationItems', super_column=':FA')
    if notifyMutations:
        yield db.batch_mutate(notifyMutations)
    log.msg('>>>>>>>> Removed friend notifications from notifications and latest.')

    # Finally, remove the connections column family.
    yield db.system_drop_column_family('connections')
    yield db.system_drop_column_family('connectionsByTag')
    log.msg('>>>>>>>> Removed the connections column family.')
Ejemplo n.º 14
0
    def _editBasicInfo(self, request):
        authInfo = request.getSession(IAuthInfo)
        myId = authInfo.username
        orgId = authInfo.organization

        userInfo = {"basic":{}}
        to_remove = []
        basicUpdatedInfo, basicUpdated = {}, False

        me = base.Entity(myId)
        yield me.fetchData([])

        # Check if any basic information is being updated.
        for cn in ("jobTitle", "name", "firstname", "lastname", "timezone"):
            val = utils.getRequestArg(request, cn)
            if not val and cn in ['name', 'jobTitle', 'timezone']:
                request.write("<script>parent.$$.alerts.error('One or more required parameters are missing')</script>")
                raise errors.MissingParams(_([cn]))
            if val:
                userInfo["basic"][cn] = val
                basicUpdatedInfo[cn] = val
            elif cn in ["firstname", "lastname"]:
                to_remove.append(cn)
                basicUpdatedInfo[cn] = ""
            if me.basic.get(cn, None) != userInfo['basic'].get(cn, None):
                basicUpdated = True

        # Update name indicies of organization.
        nameIndexKeys = [orgId]
        nameIndicesDeferreds = []
        oldNameParts = []
        newNameParts = []
        for field in ["name", "lastname", "firstname"]:
            if field in basicUpdatedInfo:
                newNameParts.extend(basicUpdatedInfo[field].split())
                oldNameParts.extend(me.basic.get(field, '').split())
                if field == 'name':
                    d1 = utils.updateDisplayNameIndex(myId, nameIndexKeys,
                                                      basicUpdatedInfo[field],
                                                      me.basic.get(field, None))
                    nameIndicesDeferreds.append(d1)
        d = utils.updateNameIndex(myId, nameIndexKeys,
                                  " ".join(newNameParts),
                                  " ".join(oldNameParts))
        nameIndicesDeferreds.append(d)

        # Avatar (display picture)
        dp = utils.getRequestArg(request, "dp", sanitize=False)
        if dp:
            avatar = yield saveAvatarItem(myId, orgId, dp)
            userInfo["basic"]["avatar"] = avatar
            me.basic["avatar"] = avatar
            avatarURI = utils.userAvatar(myId, me)
            basicUpdatedInfo["avatar"] = avatarURI
            basicUpdated = True
        if userInfo["basic"]:
            yield db.batch_insert(myId, "entities", userInfo)
            me.basic.update(userInfo['basic'])
            yield search.solr.updatePeopleIndex(myId, me, orgId)

        if to_remove:
            yield db.batch_remove({'entities':[myId]}, names=to_remove, supercolumn='basic')

        if basicUpdated:
            response = """
                        <script>
                            var data = %s;
                            if (data.avatar){
                              var imageUrl = data.avatar;
                              parent.$('#avatar').css('background-image', 'url(' + imageUrl + ')');
                            }
                            parent.$('#name').html(data.name + ', ' + data.jobTitle);
                            parent.$$.alerts.info("%s");
                        </script>
                        """ % (json.dumps(basicUpdatedInfo),  _("Profile updated"))
            request.write(response)

        # Wait for name indices to be updated.
        if nameIndicesDeferreds:
            yield defer.DeferredList(nameIndicesDeferreds)

        args = {"detail": "", "me": me}
        suggestedSections = yield self._checkProfileCompleteness(request, myId, args)
        tmp_suggested_sections = {}
        for section, items in suggestedSections.iteritems():
            if len(suggestedSections[section]) > 0:
                tmp_suggested_sections[section] = items
        args.update({'suggested_sections':tmp_suggested_sections})

        t.renderScriptBlock(request, "settings.mako", "right",
                            False, ".right-contents", "set", **args)
Ejemplo n.º 15
0
    def _deliverMessage(self, convId, recipients, timeUUID, owner):
        """To each participant in a conversation, add the conversation
        to the list of unread conversations.

        CF Changes:
        mConversations
        mConvFolders
        messages
        mAllConversations
        mDeletedConversations
        mArchivedConversations
        latest

        """
        convFolderMap = {}
        userFolderMap = {}
        toNotify = {}
        toRemove = {'latest': []}
        conv = yield db.get_slice(convId, "mConversations", ['meta'])
        conv = utils.supercolumnsToDict(conv)

        oldTimeUUID = conv['meta']['uuid']
        unread = "u:%s" % (convId)
        read = "r:%s" % (convId)

        cols = yield db.get_slice(convId, 'mConvFolders', recipients)
        cols = utils.supercolumnsToDict(cols)
        for recipient in recipients:
            deliverToInbox = True
            # for a new message, mConvFolders will be empty
            # so recipient may not necessarily be present in cols
            if recipient != owner:
                toNotify[recipient] = {'latest': {'messages': {timeUUID: convId}}}
            toRemove['latest'].append(recipient)

            for folder in cols.get(recipient, []):
                cf = self._folders[folder] if folder in self._folders else folder
                yield db.remove(recipient, cf, oldTimeUUID)
                if cf == 'mDeletedConversations':
                    #don't add to recipient's inbox if the conv is deleted.
                    deliverToInbox = False
                else:
                    yield db.remove(convId, 'mConvFolders', folder, recipient)
            if deliverToInbox:
                val = unread if recipient != owner else read
                convFolderMap[recipient] = {'mAllConversations': {timeUUID: val}}
                userFolderMap[recipient] = {'mAllConversations': ''}
                if recipient != owner:
                    convFolderMap[recipient]['mUnreadConversations'] = {timeUUID: unread}
                    userFolderMap[recipient]['mUnreadConversations'] = ''

            else:
                val = unread if recipient != owner else read
                convFolderMap[recipient] = {'mDeletedConversations': {timeUUID: val}}

        yield db.batch_mutate(convFolderMap)
        yield db.batch_insert(convId, "mConvFolders", userFolderMap)
        if toRemove['latest'] and oldTimeUUID != timeUUID:
            yield db.batch_remove(toRemove, names=[oldTimeUUID], supercolumn="messages")
        if toNotify:
            yield db.batch_mutate(toNotify)
Ejemplo n.º 16
0
    def _rsvp(self, request):
        (appchange, script, args, myId) = yield self._getBasicArgs(request)
        landing = not self._ajax
        orgId = args['orgId']

        response = utils.getRequestArg(request, 'response')
        deferreds = []
        prevResponse = ""

        if not response or response not in ('yes', 'maybe', 'no'):
            raise errors.InvalidRequest()

        convId, conv = yield utils.getValidItemId(request, "id",
                                                  columns=["invitees"])

        if not conv:
            raise errors.MissingParams([_("Event ID")])

        if ("type" in conv["meta"] and conv["meta"]["type"] != "event"):
            raise errors.InvalidRequest("Not a valid event")

        #Find out if already stored response is the same as this one. Saves
        # quite a few queries
        rsvp_names = ["%s:%s" %(x, myId) for x in ['yes', 'no', 'maybe']]
        cols = yield db.get_slice(convId, "eventResponses", names=rsvp_names)
        if cols:
            prevResponse = cols[0].column.name.split(":", 1)[0]

        if prevResponse == response:
            defer.returnValue(0)

        starttime = int(conv["meta"]["event_startTime"])
        endtime = int(conv["meta"]["event_endTime"])
        starttimeUUID = utils.uuid1(timestamp=starttime)
        starttimeUUID = starttimeUUID.bytes
        endtimeUUID = utils.uuid1(timestamp=endtime)
        endtimeUUID = endtimeUUID.bytes

        #Now insert the event in the user's agenda list if the user has
        # never responded to this event or the user is not in the invited list.
        #In the second case the agenda was already updated when creating the
        # event
        if prevResponse == "" and myId not in conv["invitees"].keys():
            d1 = db.insert(myId, "userAgenda", convId, starttimeUUID)
            d2 = db.insert(myId, "userAgenda", convId, endtimeUUID)
            d3 = db.insert("%s:%s" %(myId, convId), "userAgendaMap", "",
                          starttimeUUID)
            d4 = db.insert("%s:%s" %(myId, convId), "userAgendaMap", "",
                          endtimeUUID)
            deferreds.extend([d1, d3, d2, d4])

        #Remove any old responses to this event by this user.
        yield db.batch_remove({'eventResponses': [convId]}, names=rsvp_names)

        #Now insert the user's new response.
        d = db.insert(convId, "eventResponses", "", "%s:%s" %(response, myId))
        deferreds.append(d)

        if script:
            #Update the inline status of the rsvp status
            if response == "yes":
                rsp = _("You are attending")
            elif response == "no":
                rsp = _("You are not attending")
            elif response == "maybe":
                rsp = _("You may attend")

            request.write("$('#event-rsvp-status-%s').text('%s');"
                                                            %(convId, rsp))
            request.write("$('#conv-%s .event-join-decline').text('%s');"
                                                            %(convId, rsp))

        if deferreds:
            res = yield defer.DeferredList(deferreds)

        if script:
            args.update({"items":{convId:conv}, "convId":convId})
            entityIds = yield event.fetchData(args, convId)
            entities = base.EntitySet(entityIds)
            yield entities.fetchData()
            args["entities"] = entities

            t.renderScriptBlock(request, "event.mako", "event_meta",
                                landing, "#item-meta", "set", **args)

        # Push Feed Updates
        responseType = "E"
        convMeta = conv["meta"]
        convType = convMeta["type"]
        convOwnerId = convMeta["owner"]
        commentSnippet = convMeta["event_title"]
        itemId = convId
        convACL = convMeta["acl"]
        extraEntities = [convMeta["owner"]]
        # Importing social.feed at the beginning of the module leads to
        # a cyclic dependency as feed in turn imports plugins.
        from social.core import Feed

        if response == "yes":
            timeUUID = uuid.uuid1().bytes

            # Add user to the followers list of parent item
            yield db.insert(convId, "items", "", myId, "followers")

            # Update user's feed, feedItems, feed_*
            userItemValue = ":".join([responseType, itemId, convId, convType,
                                      convOwnerId, commentSnippet])
            yield db.insert(myId, "userItems", userItemValue, timeUUID)
            yield db.insert(myId, "userItems_event", userItemValue, timeUUID)

            # Push to feed
            feedItemVal = "%s:%s:%s:%s" % (responseType, myId, itemId,
                                            ','.join(extraEntities))
            yield Feed.push(myId, orgId, convId, conv, timeUUID, feedItemVal)
        elif prevResponse != "":
            rsvpTimeUUID = None
            cols = yield db.get_slice(myId, "userItems")
            cols = utils.columnsToDict(cols)
            for k, v in cols.iteritems():
                if v.startswith("E"):
                    rsvpTimeUUID = k

            if rsvpTimeUUID:
                # Remove update if user changes RSVP to no/maybe from yes.
                # Do not update if user had RSVPed to this event.
                feedUpdateVal = "%s:%s:%s:%s" % (responseType, myId, itemId,
                                                 convOwnerId)
                yield Feed.unpush(myId, orgId, convId, conv, feedUpdateVal)

                # FIX: if user updates more than one item at exactly same time,
                #      one of the updates will overwrite the other. Fix it.
                yield db.remove(myId, "userItems", rsvpTimeUUID)
                yield db.remove(myId, "userItems_event", rsvpTimeUUID)

        if myId != convOwnerId and response == "yes":
            timeUUID = uuid.uuid1().bytes
            yield _notify("EA", convId, timeUUID, convType=convType,
                              convOwnerId=convOwnerId, myId=myId, me=args["me"])
Ejemplo n.º 17
0
    def _removeMembers(self, request):
        """This method allows the current user to remove another participant
        to this conversation.

        Keyword Arguments:
        newMembers: A list of members who will be added to this conversation.
        convId: The id of the conversation to which these new members will be
            added as participants.

        CF Changes:
        mConversations
        latest

        """
        myId = request.getSession(IAuthInfo).username
        orgId = request.getSession(IAuthInfo).organization
        members, body, subject, convId = self._parseComposerArgs(request)

        if not (convId and  members):
            raise errors.MissingParams([])

        conv = yield db.get_slice(convId, "mConversations")
        conv = utils.supercolumnsToDict(conv)
        subject = conv['meta'].get('subject', None)
        participants = conv.get('participants', {}).keys()
        if not conv:
            raise errors.InvalidMessage(convId)
        if myId not in participants:
            raise errors.MessageAccessDenied(convId)

        cols = yield db.multiget_slice(members, "entities", ['basic'])
        people = utils.multiSuperColumnsToDict(cols)
        members = set([userId for userId in people if people[userId] and \
                          people[userId]["basic"]["org"] == orgId])
        members = members.intersection(participants)

        if len(members) == len(participants):
            members.remove(conv['meta']['owner'])

        deferreds = []
        if members:
            d = db.batch_remove({"mConversations": [convId]},
                                    names=members,
                                    supercolumn='participants')
            deferreds.append(d)

            cols = yield db.get_slice(convId, 'mConvFolders', members)
            cols = utils.supercolumnsToDict(cols)
            for recipient in cols:
                for folder in cols[recipient]:
                    cf = self._folders[folder] if folder in self._folders else folder
                    d = db.remove(recipient, cf, conv['meta']['uuid'])
                    deferreds.append(d)
            #update latest- messages-count
            deferreds.append(db.batch_remove({"latest": members},
                                             names=[conv['meta']['uuid']],
                                             supercolumn='messages'))
            if deferreds:
                yield deferreds

        mailNotificants = set(participants) - members - set([myId])
        if mailNotificants and members:
            toFetchEntities = mailNotificants.union([myId, orgId]).union(members)
            entities = base.EntitySet(toFetchEntities)
            yield entities.fetchData()
            data = {"entities": entities}
            data["orgId"] = orgId
            data["convId"] = convId
            data["removedMembers"] = members
            data["subject"] = subject
            data["_fromName"] = entities[myId].basic['name']
            yield notifications.notify(mailNotificants, ":MA", myId, **data)
Ejemplo n.º 18
0
    def _editPersonalInfo(self, request):
        # Personal information about the user
        myId = request.getSession(IAuthInfo).username
        orgId = request.getSession(IAuthInfo).organization
        data = {}
        to_remove = []

        me = yield db.get_slice(myId, 'entities')
        me = base.Entity(myId)
        yield me.fetchData([])
        dob_day = utils.getRequestArg(request, "dob_day") or None
        dob_mon = utils.getRequestArg(request, "dob_mon") or None
        dob_year = utils.getRequestArg(request, "dob_year") or None

        if dob_day and dob_mon and dob_year:
            try:
                dateStr = "%s/%s/%s" % (dob_day, dob_mon, dob_year)
                date = time.strptime(dateStr, "%d/%m/%Y")
                if date.tm_year < time.localtime().tm_year:
                    dob_day = "%02d" % date.tm_mday
                    dob_mon = "%02d" % date.tm_mon
                    data["birthday"] = "%s%s%s" % (dob_year, dob_mon, dob_day)
            except ValueError:
                raise errors.InvalidRequest(_('Please select a valid Date of Birth'))
        else:
            to_remove.append('birthday')

        columnNames = ['email', 'phone', 'mobile', 'hometown', 'currentCity']
        for name in columnNames:
            val = utils.getRequestArg(request, name)
            if val:
                data[name] = val
            else:
                to_remove.append(name)
        if data:
            yield db.batch_insert(myId, "entities", {"personal": data})
        if to_remove:
            yield db.batch_remove({'entities':[myId]}, names=to_remove, supercolumn='personal')

        if 'phone' in data and not re.match('^\+?[0-9x\- ]{5,20}$', data['phone']):
            raise errors.InvalidRequest(_('Phone numbers can only have numerals, hyphens, spaces and a plus sign'))

        if 'mobile' in data and not re.match('^\+?[0-9x\- ]{5,20}$', data['mobile']):
            raise errors.InvalidRequest(_('Phone numbers can only have numerals, hyphens, spaces and a plus sign'))

        columnNames.append('birthday')
        personalInfo =  me.get('personal', {})
        if any([personalInfo.get(x, None) != data.get(x, None) for x in columnNames]):
            request.write('$$.alerts.info("%s");' % _('Profile updated'))

        args = {"detail": "", "me": me}
        suggestedSections = yield self._checkProfileCompleteness(request, myId, args)
        tmp_suggested_sections = {}
        for section, items in suggestedSections.iteritems():
            if len(suggestedSections[section]) > 0:
                tmp_suggested_sections[section] = items
        args.update({'suggested_sections':tmp_suggested_sections})

        t.renderScriptBlock(request, "settings.mako", "right",
                            False, ".right-contents", "set", **args)
        me.update({'personal':data})
        yield search.solr.updatePeopleIndex(myId, me, orgId)