001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.social.service.impl;
016    
017    import com.liferay.portal.kernel.cache.MultiVMPoolUtil;
018    import com.liferay.portal.kernel.cache.PortalCache;
019    import com.liferay.portal.kernel.dao.orm.QueryUtil;
020    import com.liferay.portal.kernel.exception.PortalException;
021    import com.liferay.portal.kernel.lock.LockProtectedAction;
022    import com.liferay.portal.kernel.transaction.Propagation;
023    import com.liferay.portal.kernel.transaction.Transactional;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.StringUtil;
026    import com.liferay.portal.kernel.util.Tuple;
027    import com.liferay.portal.model.Group;
028    import com.liferay.portal.model.User;
029    import com.liferay.portal.util.PortalUtil;
030    import com.liferay.portal.util.PropsValues;
031    import com.liferay.portlet.asset.model.AssetEntry;
032    import com.liferay.portlet.social.model.SocialAchievement;
033    import com.liferay.portlet.social.model.SocialActivity;
034    import com.liferay.portlet.social.model.SocialActivityConstants;
035    import com.liferay.portlet.social.model.SocialActivityCounter;
036    import com.liferay.portlet.social.model.SocialActivityCounterConstants;
037    import com.liferay.portlet.social.model.SocialActivityCounterDefinition;
038    import com.liferay.portlet.social.model.SocialActivityDefinition;
039    import com.liferay.portlet.social.model.SocialActivityLimit;
040    import com.liferay.portlet.social.model.SocialActivityProcessor;
041    import com.liferay.portlet.social.model.impl.SocialActivityImpl;
042    import com.liferay.portlet.social.service.base.SocialActivityCounterLocalServiceBaseImpl;
043    import com.liferay.portlet.social.service.persistence.SocialActivityCounterFinder;
044    import com.liferay.portlet.social.util.SocialCounterPeriodUtil;
045    
046    import java.util.ArrayList;
047    import java.util.Arrays;
048    import java.util.Collections;
049    import java.util.HashMap;
050    import java.util.List;
051    import java.util.Map;
052    
053    /**
054     * The social activity counter local service. This service is responsible for
055     * creating and/or incrementing counters in response to an activity. It also
056     * provides methods for querying activity counters within a time period.
057     *
058     * <p>
059     * Under normal circumstances only the {@link
060     * #addActivityCounters(SocialActivity)} should be called directly and even that
061     * is usually not necessary as it is automatically called by the social activity
062     * service.
063     * </p>
064     *
065     * @author Zsolt Berentey
066     * @author Shuyang Zhou
067     */
068    public class SocialActivityCounterLocalServiceImpl
069            extends SocialActivityCounterLocalServiceBaseImpl {
070    
071            /**
072             * Adds an activity counter specifying a previous activity and period
073             * length.
074             *
075             * <p>
076             * This method uses the lock service to guard against multiple threads
077             * trying to insert the same counter because this service is called
078             * asynchronously from the social activity service.
079             * </p>
080             *
081             * @param  groupId the primary key of the group
082             * @param  classNameId the primary key of the entity's class this counter
083             *         belongs to
084             * @param  classPK the primary key of the entity this counter belongs to
085             * @param  name the counter name
086             * @param  ownerType the counter's owner type. Acceptable values are
087             *         <code>TYPE_ACTOR</code>, <code>TYPE_ASSET</code> and
088             *         <code>TYPE_CREATOR</code> defined in {@link
089             *         SocialActivityCounterConstants}.
090             * @param  totalValue the counter's total value (optionally <code>0</code>)
091             * @param  previousActivityCounterId the primary key of the activity counter
092             *         for the previous time period (optionally <code>0</code>, if this
093             *         is the first)
094             * @param  periodLength the period length in days,
095             *         <code>PERIOD_LENGTH_INFINITE</code> for never ending counters or
096             *         <code>PERIOD_LENGTH_SYSTEM</code> for the period length defined
097             *         in <code>portal-ext.properties</code>. For more information see
098             *         {@link SocialActivityCounterConstants}.
099             * @return the added activity counter
100             */
101            @Override
102            @Transactional(propagation = Propagation.REQUIRES_NEW)
103            public SocialActivityCounter addActivityCounter(
104                            long groupId, long classNameId, long classPK, String name,
105                            int ownerType, int totalValue, long previousActivityCounterId,
106                            int periodLength)
107                    throws PortalException {
108    
109                    SocialActivityCounter activityCounter = null;
110    
111                    if (previousActivityCounterId != 0) {
112                            activityCounter = socialActivityCounterPersistence.findByPrimaryKey(
113                                    previousActivityCounterId);
114    
115                            if (periodLength ==
116                                            SocialActivityCounterConstants.PERIOD_LENGTH_SYSTEM) {
117    
118                                    activityCounter.setEndPeriod(
119                                            SocialCounterPeriodUtil.getStartPeriod() - 1);
120                            }
121                            else {
122                                    activityCounter.setEndPeriod(
123                                            activityCounter.getStartPeriod() + periodLength - 1);
124                            }
125    
126                            socialActivityCounterPersistence.update(activityCounter);
127                    }
128    
129                    activityCounter = socialActivityCounterPersistence.fetchByG_C_C_N_O_E(
130                            groupId, classNameId, classPK, name, ownerType,
131                            SocialActivityCounterConstants.END_PERIOD_UNDEFINED, false);
132    
133                    if (activityCounter != null) {
134                            return activityCounter;
135                    }
136    
137                    Group group = groupPersistence.findByPrimaryKey(groupId);
138    
139                    long activityCounterId = counterLocalService.increment();
140    
141                    activityCounter = socialActivityCounterPersistence.create(
142                            activityCounterId);
143    
144                    activityCounter.setGroupId(groupId);
145                    activityCounter.setCompanyId(group.getCompanyId());
146                    activityCounter.setClassNameId(classNameId);
147                    activityCounter.setClassPK(classPK);
148                    activityCounter.setName(name);
149                    activityCounter.setOwnerType(ownerType);
150                    activityCounter.setTotalValue(totalValue);
151    
152                    if (periodLength ==
153                                    SocialActivityCounterConstants.PERIOD_LENGTH_SYSTEM) {
154    
155                            activityCounter.setStartPeriod(
156                                    SocialCounterPeriodUtil.getStartPeriod());
157                    }
158                    else {
159                            activityCounter.setStartPeriod(
160                                    SocialCounterPeriodUtil.getActivityDay());
161                    }
162    
163                    activityCounter.setEndPeriod(
164                            SocialActivityCounterConstants.END_PERIOD_UNDEFINED);
165                    activityCounter.setActive(true);
166    
167                    socialActivityCounterPersistence.update(activityCounter);
168    
169                    return activityCounter;
170            }
171    
172            /**
173             * Adds or increments activity counters related to an activity.
174             *
175             * </p>
176             * This method is called asynchronously from the social activity service
177             * when the user performs an activity defined in
178             * </code>liferay-social.xml</code>.
179             * </p>
180             *
181             * <p>
182             * This method first calls the activity processor class, if there is one
183             * defined for the activity, checks for limits and increments all the
184             * counters that belong to the activity. Afterwards, it processes the
185             * activity with respect to achievement classes, if any. Lastly it
186             * increments the built-in <code>user.activities</code> and
187             * <code>asset.activities</code> counters.
188             * </p>
189             *
190             * @param activity the social activity
191             */
192            @Override
193            public void addActivityCounters(SocialActivity activity)
194                    throws PortalException {
195    
196                    if (!socialActivitySettingLocalService.isEnabled(
197                                    activity.getGroupId(), activity.getClassNameId())) {
198    
199                            return;
200                    }
201    
202                    if (!socialActivitySettingLocalService.isEnabled(
203                                    activity.getGroupId(), activity.getClassNameId(),
204                                    activity.getClassPK())) {
205    
206                            return;
207                    }
208    
209                    if ((activity.getType() ==
210                                    SocialActivityConstants.TYPE_MOVE_ATTACHMENT_TO_TRASH) ||
211                            (activity.getType() ==
212                                    SocialActivityConstants.TYPE_MOVE_TO_TRASH)) {
213    
214                            disableActivityCounters(
215                                    activity.getClassNameId(), activity.getClassPK());
216    
217                            return;
218                    }
219    
220                    if ((activity.getType() ==
221                                    SocialActivityConstants.TYPE_RESTORE_ATTACHMENT_FROM_TRASH) ||
222                            (activity.getType() ==
223                                    SocialActivityConstants.TYPE_RESTORE_FROM_TRASH)) {
224    
225                            enableActivityCounters(
226                                    activity.getClassNameId(), activity.getClassPK());
227    
228                            return;
229                    }
230    
231                    User user = userPersistence.findByPrimaryKey(activity.getUserId());
232    
233                    SocialActivityDefinition activityDefinition =
234                            socialActivitySettingLocalService.getActivityDefinition(
235                                    activity.getGroupId(), activity.getClassName(),
236                                    activity.getType());
237    
238                    if ((activityDefinition == null) ||
239                            !activityDefinition.isCountersEnabled()) {
240    
241                            return;
242                    }
243    
244                    SocialActivityProcessor activityProcessor =
245                            activityDefinition.getActivityProcessor();
246    
247                    if (activityProcessor != null) {
248                            activityProcessor.processActivity(activity);
249                    }
250    
251                    AssetEntry assetEntry = activity.getAssetEntry();
252    
253                    User assetEntryUser = userPersistence.findByPrimaryKey(
254                            assetEntry.getUserId());
255    
256                    List<SocialActivityCounter> activityCounters = new ArrayList<>();
257    
258                    for (SocialActivityCounterDefinition activityCounterDefinition :
259                                    activityDefinition.getActivityCounterDefinitions()) {
260    
261                            if (isAddActivityCounter(
262                                            user, assetEntryUser, assetEntry,
263                                            activityCounterDefinition)) {
264    
265                                    SocialActivityCounter activityCounter = addActivityCounter(
266                                            activity.getGroupId(), user, activity,
267                                            activityCounterDefinition);
268    
269                                    activityCounters.add(activityCounter);
270                            }
271                    }
272    
273                    SocialActivityCounter assetActivitiesCounter = null;
274    
275                    if (!assetEntryUser.isDefaultUser() && assetEntryUser.isActive() &&
276                            assetEntry.isVisible()) {
277    
278                            assetActivitiesCounter = addAssetActivitiesCounter(activity);
279                    }
280    
281                    SocialActivityCounter userActivitiesCounter = null;
282    
283                    if (!user.isDefaultUser() && user.isActive()) {
284                            userActivitiesCounter = addUserActivitiesCounter(activity);
285                    }
286    
287                    for (SocialActivityCounter activityCounter : activityCounters) {
288                            SocialActivityCounterDefinition activityCounterDefinition =
289                                    activityDefinition.getActivityCounterDefinition(
290                                            activityCounter.getName());
291    
292                            if (checkActivityLimit(user, activity, activityCounterDefinition)) {
293                                    incrementActivityCounter(
294                                            activityCounter, activityCounterDefinition);
295                            }
296                    }
297    
298                    if (assetActivitiesCounter != null) {
299                            incrementActivityCounter(
300                                    assetActivitiesCounter,
301                                    _assetActivitiesActivityCounterDefinition);
302                    }
303    
304                    if (userActivitiesCounter != null) {
305                            incrementActivityCounter(
306                                    userActivitiesCounter,
307                                    _userActivitiesActivityCounterDefinition);
308                    }
309    
310                    for (SocialAchievement achievement :
311                                    activityDefinition.getAchievements()) {
312    
313                            achievement.processActivity(activity);
314                    }
315            }
316    
317            /**
318             * Deletes all activity counters, limits, and settings related to the asset.
319             *
320             * <p>
321             * This method subtracts the asset's popularity from the owner's
322             * contribution points. It also creates a new contribution period if the
323             * latest one does not belong to the current period.
324             * </p>
325             *
326             * @param assetEntry the asset entry
327             */
328            @Override
329            public void deleteActivityCounters(AssetEntry assetEntry)
330                    throws PortalException {
331    
332                    if (assetEntry == null) {
333                            return;
334                    }
335    
336                    adjustUserContribution(assetEntry, false);
337    
338                    socialActivityCounterPersistence.removeByC_C(
339                            assetEntry.getClassNameId(), assetEntry.getClassPK());
340    
341                    socialActivityLimitPersistence.removeByC_C(
342                            assetEntry.getClassNameId(), assetEntry.getClassPK());
343    
344                    socialActivitySettingLocalService.deleteActivitySetting(
345                            assetEntry.getGroupId(), assetEntry.getClassName(),
346                            assetEntry.getClassPK());
347    
348                    clearFinderCache();
349            }
350    
351            /**
352             * Deletes all activity counters, limits, and settings related to the entity
353             * identified by the class name ID and class primary key.
354             *
355             * @param classNameId the primary key of the entity's class
356             * @param classPK the primary key of the entity
357             */
358            @Override
359            public void deleteActivityCounters(long classNameId, long classPK)
360                    throws PortalException {
361    
362                    String className = PortalUtil.getClassName(classNameId);
363    
364                    if (!className.equals(User.class.getName())) {
365                            AssetEntry assetEntry = assetEntryLocalService.fetchEntry(
366                                    className, classPK);
367    
368                            deleteActivityCounters(assetEntry);
369                    }
370                    else {
371                            socialActivityCounterPersistence.removeByC_C(classNameId, classPK);
372    
373                            socialActivityLimitPersistence.removeByUserId(classPK);
374                    }
375    
376                    clearFinderCache();
377            }
378    
379            /**
380             * Deletes all activity counters for the entity identified by the class name
381             * and class primary key.
382             *
383             * @param className the entity's class name
384             * @param classPK the primary key of the entity
385             */
386            @Override
387            public void deleteActivityCounters(String className, long classPK)
388                    throws PortalException {
389    
390                    if (!className.equals(User.class.getName())) {
391                            AssetEntry assetEntry = assetEntryLocalService.fetchEntry(
392                                    className, classPK);
393    
394                            deleteActivityCounters(assetEntry);
395                    }
396                    else {
397                            long classNameId = classNameLocalService.getClassNameId(className);
398    
399                            socialActivityCounterPersistence.removeByC_C(classNameId, classPK);
400    
401                            socialActivityLimitPersistence.removeByUserId(classPK);
402                    }
403    
404                    clearFinderCache();
405            }
406    
407            /**
408             * Disables all the counters of an asset identified by the class name ID and
409             * class primary key.
410             *
411             * <p>
412             * This method is used by the recycle bin to disable all counters of assets
413             * put into the recycle bin. It adjusts the owner's contribution score.
414             * </p>
415             *
416             * @param classNameId the primary key of the asset's class
417             * @param classPK the primary key of the asset
418             */
419            @Override
420            public void disableActivityCounters(long classNameId, long classPK)
421                    throws PortalException {
422    
423                    String className = PortalUtil.getClassName(classNameId);
424    
425                    disableActivityCounters(className, classPK);
426            }
427    
428            /**
429             * Disables all the counters of an asset identified by the class name and
430             * class primary key.
431             *
432             * <p>
433             * This method is used by the recycle bin to disable all counters of assets
434             * put into the recycle bin. It adjusts the owner's contribution score.
435             * </p>
436             *
437             * @param className the asset's class name
438             * @param classPK the primary key of the asset
439             */
440            @Override
441            public void disableActivityCounters(String className, long classPK)
442                    throws PortalException {
443    
444                    AssetEntry assetEntry = assetEntryLocalService.fetchEntry(
445                            className, classPK);
446    
447                    if (assetEntry == null) {
448                            return;
449                    }
450    
451                    List<SocialActivityCounter> activityCounters =
452                            socialActivityCounterPersistence.findByC_C(
453                                    assetEntry.getClassNameId(), classPK);
454    
455                    adjustUserContribution(assetEntry, false);
456    
457                    for (SocialActivityCounter activityCounter : activityCounters) {
458                            if (activityCounter.isActive()) {
459                                    activityCounter.setActive(false);
460    
461                                    socialActivityCounterPersistence.update(activityCounter);
462                            }
463                    }
464    
465                    clearFinderCache();
466            }
467    
468            /**
469             * Enables all activity counters of an asset identified by the class name ID
470             * and class primary key.
471             *
472             * <p>
473             * This method is used by the recycle bin to enable all counters of assets
474             * restored from the recycle bin. It adjusts the owner's contribution score.
475             * </p>
476             *
477             * @param classNameId the primary key of the asset's class
478             * @param classPK the primary key of the asset
479             */
480            @Override
481            public void enableActivityCounters(long classNameId, long classPK)
482                    throws PortalException {
483    
484                    String className = PortalUtil.getClassName(classNameId);
485    
486                    enableActivityCounters(className, classPK);
487            }
488    
489            /**
490             * Enables all the counters of an asset identified by the class name and
491             * class primary key.
492             *
493             * <p>
494             * This method is used by the recycle bin to enable all counters of assets
495             * restored from the recycle bin. It adjusts the owner's contribution score.
496             * </p>
497             *
498             * @param className the asset's class name
499             * @param classPK the primary key of the asset
500             */
501            @Override
502            public void enableActivityCounters(String className, long classPK)
503                    throws PortalException {
504    
505                    AssetEntry assetEntry = assetEntryLocalService.fetchEntry(
506                            className, classPK);
507    
508                    if (assetEntry == null) {
509                            return;
510                    }
511    
512                    List<SocialActivityCounter> activityCounters =
513                            socialActivityCounterPersistence.findByC_C(
514                                    assetEntry.getClassNameId(), classPK);
515    
516                    adjustUserContribution(assetEntry, true);
517    
518                    for (SocialActivityCounter activityCounter : activityCounters) {
519                            if (!activityCounter.isActive()) {
520                                    activityCounter.setActive(true);
521    
522                                    socialActivityCounterPersistence.update(activityCounter);
523                            }
524                    }
525    
526                    clearFinderCache();
527            }
528    
529            /**
530             * Returns the activity counter with the given name, owner, and end period
531             * that belong to the given entity.
532             *
533             * @param  groupId the primary key of the group
534             * @param  classNameId the primary key of the entity's class
535             * @param  classPK the primary key of the entity
536             * @param  name the counter name
537             * @param  ownerType the owner type
538             * @param  endPeriod the end period, <code>-1</code> for the latest one
539             * @return the matching activity counter
540             */
541            @Override
542            public SocialActivityCounter fetchActivityCounterByEndPeriod(
543                    long groupId, long classNameId, long classPK, String name,
544                    int ownerType, int endPeriod) {
545    
546                    return socialActivityCounterPersistence.fetchByG_C_C_N_O_E(
547                            groupId, classNameId, classPK, name, ownerType, endPeriod);
548            }
549    
550            /**
551             * Returns the activity counter with the given name, owner, and start period
552             * that belong to the given entity.
553             *
554             * @param  groupId the primary key of the group
555             * @param  classNameId the primary key of the entity's class
556             * @param  classPK the primary key of the entity
557             * @param  name the counter name
558             * @param  ownerType the owner type
559             * @param  startPeriod the start period
560             * @return the matching activity counter
561             */
562            @Override
563            public SocialActivityCounter fetchActivityCounterByStartPeriod(
564                    long groupId, long classNameId, long classPK, String name,
565                    int ownerType, int startPeriod) {
566    
567                    return socialActivityCounterPersistence.fetchByG_C_C_N_O_S(
568                            groupId, classNameId, classPK, name, ownerType, startPeriod);
569            }
570    
571            /**
572             * Returns the latest activity counter with the given name and owner that
573             * belong to the given entity.
574             *
575             * @param  groupId the primary key of the group
576             * @param  classNameId the primary key of the entity's class
577             * @param  classPK the primary key of the entity
578             * @param  name the counter name
579             * @param  ownerType the owner type
580             * @return the matching activity counter
581             */
582            @Override
583            public SocialActivityCounter fetchLatestActivityCounter(
584                    long groupId, long classNameId, long classPK, String name,
585                    int ownerType) {
586    
587                    return socialActivityCounterPersistence.fetchByG_C_C_N_O_E(
588                            groupId, classNameId, classPK, name, ownerType,
589                            SocialActivityCounterConstants.END_PERIOD_UNDEFINED);
590            }
591    
592            /**
593             * Returns all the activity counters with the given name and period offsets.
594             *
595             * <p>
596             * The start and end offsets can belong to different periods. This method
597             * groups the counters by name and returns the sum of their current values.
598             * </p>
599             *
600             * @param  groupId the primary key of the group
601             * @param  name the counter name
602             * @param  startOffset the offset for the start period
603             * @param  endOffset the offset for the end period
604             * @return the matching activity counters
605             */
606            @Override
607            public List<SocialActivityCounter> getOffsetActivityCounters(
608                    long groupId, String name, int startOffset, int endOffset) {
609    
610                    int startPeriod = SocialCounterPeriodUtil.getStartPeriod(startOffset);
611                    int endPeriod = SocialCounterPeriodUtil.getEndPeriod(endOffset);
612    
613                    return getPeriodActivityCounters(groupId, name, startPeriod, endPeriod);
614            }
615    
616            /**
617             * Returns the distribution of the activity counters with the given name and
618             * period offsets.
619             *
620             * <p>
621             * The start and end offsets can belong to different periods. This method
622             * groups the counters by their owner entity (usually some asset) and
623             * returns a counter for each entity class with the sum of the counters'
624             * current values.
625             * </p>
626             *
627             * @param  groupId the primary key of the group
628             * @param  name the counter name
629             * @param  startOffset the offset for the start period
630             * @param  endOffset the offset for the end period
631             * @return the distribution of matching activity counters
632             */
633            @Override
634            public List<SocialActivityCounter> getOffsetDistributionActivityCounters(
635                    long groupId, String name, int startOffset, int endOffset) {
636    
637                    int startPeriod = SocialCounterPeriodUtil.getStartPeriod(startOffset);
638                    int endPeriod = SocialCounterPeriodUtil.getEndPeriod(endOffset);
639    
640                    return getPeriodDistributionActivityCounters(
641                            groupId, name, startPeriod, endPeriod);
642            }
643    
644            /**
645             * Returns all the activity counters with the given name and time period.
646             *
647             * <p>
648             * The start and end period values can belong to different periods. This
649             * method groups the counters by name and returns the sum of their current
650             * values.
651             * </p>
652             *
653             * @param  groupId the primary key of the group
654             * @param  name the counter name
655             * @param  startPeriod the start period
656             * @param  endPeriod the end period
657             * @return the matching activity counters
658             */
659            @Override
660            public List<SocialActivityCounter> getPeriodActivityCounters(
661                    long groupId, String name, int startPeriod, int endPeriod) {
662    
663                    if (endPeriod == SocialActivityCounterConstants.END_PERIOD_UNDEFINED) {
664                            endPeriod = SocialCounterPeriodUtil.getEndPeriod();
665                    }
666    
667                    int offset = SocialCounterPeriodUtil.getOffset(endPeriod);
668    
669                    int periodLength = SocialCounterPeriodUtil.getPeriodLength(offset);
670    
671                    return socialActivityCounterFinder.findAC_ByG_N_S_E_1(
672                            groupId, name, startPeriod, endPeriod, periodLength);
673            }
674    
675            /**
676             * Returns the distribution of activity counters with the given name and
677             * time period.
678             *
679             * <p>
680             * The start and end period values can belong to different periods. This
681             * method groups the counters by their owner entity (usually some asset) and
682             * returns a counter for each entity class with the sum of the counters'
683             * current values.
684             * </p>
685             *
686             * @param  groupId the primary key of the group
687             * @param  name the counter name
688             * @param  startPeriod the start period
689             * @param  endPeriod the end period
690             * @return the distribution of matching activity counters
691             */
692            @Override
693            public List<SocialActivityCounter> getPeriodDistributionActivityCounters(
694                    long groupId, String name, int startPeriod, int endPeriod) {
695    
696                    int offset = SocialCounterPeriodUtil.getOffset(endPeriod);
697    
698                    int periodLength = SocialCounterPeriodUtil.getPeriodLength(offset);
699    
700                    return socialActivityCounterFinder.findAC_ByG_N_S_E_2(
701                            groupId, name, startPeriod, endPeriod, periodLength);
702            }
703    
704            /**
705             * Returns the range of tuples that contain users and a list of activity
706             * counters.
707             *
708             * <p>
709             * The counters returned for each user are passed to this method in the
710             * selectedNames array. The method also accepts an array of counter names
711             * that are used to rank the users.
712             * </p>
713             *
714             * <p>
715             * Useful when paginating results. Returns a maximum of <code>end -
716             * start</code> instances. <code>start</code> and <code>end</code> are not
717             * primary keys, they are indexes in the result set. Thus, <code>0</code>
718             * refers to the first result in the set. Setting both <code>start</code>
719             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
720             * result set.
721             * </p>
722             *
723             * @param  groupId the primary key of the group
724             * @param  rankingNames the ranking counter names
725             * @param  selectedNames the counter names that will be returned with each
726             *         user
727             * @param  start the lower bound of the range of results
728             * @param  end the upper bound of the range of results (not inclusive)
729             * @return the range of matching tuples
730             */
731            @Override
732            public List<Tuple> getUserActivityCounters(
733                    long groupId, String[] rankingNames, String[] selectedNames, int start,
734                    int end) {
735    
736                    List<Long> userIds = socialActivityCounterFinder.findU_ByG_N(
737                            groupId, rankingNames, start, end);
738    
739                    if (userIds.isEmpty()) {
740                            return Collections.emptyList();
741                    }
742    
743                    Tuple[] userActivityCounters = new Tuple[userIds.size()];
744    
745                    List<SocialActivityCounter> activityCounters =
746                            socialActivityCounterFinder.findAC_By_G_C_C_N_S_E(
747                                    groupId, userIds, selectedNames, QueryUtil.ALL_POS,
748                                    QueryUtil.ALL_POS);
749    
750                    long userId = 0;
751                    Map<String, SocialActivityCounter> activityCountersMap = null;
752    
753                    for (SocialActivityCounter activityCounter : activityCounters) {
754                            if (userId != activityCounter.getClassPK()) {
755                                    userId = activityCounter.getClassPK();
756                                    activityCountersMap = new HashMap<>();
757    
758                                    Tuple userActivityCounter = new Tuple(
759                                            userId, activityCountersMap);
760    
761                                    for (int i = 0; i < userIds.size(); i++) {
762                                            long curUserId = userIds.get(i);
763    
764                                            if (userId == curUserId) {
765                                                    userActivityCounters[i] = userActivityCounter;
766    
767                                                    break;
768                                            }
769                                    }
770                            }
771    
772                            activityCountersMap.put(activityCounter.getName(), activityCounter);
773                    }
774    
775                    return Arrays.asList(userActivityCounters);
776            }
777    
778            /**
779             * Returns the number of users having a rank based on the given counters.
780             *
781             * @param  groupId the primary key of the group
782             * @param  rankingNames the ranking counter names
783             * @return the number of matching users
784             */
785            @Override
786            public int getUserActivityCountersCount(
787                    long groupId, String[] rankingNames) {
788    
789                    return socialActivityCounterFinder.countU_ByG_N(groupId, rankingNames);
790            }
791    
792            /**
793             * Increments the <code>user.achievements</code> counter for a user.
794             *
795             * <p>
796             * This method should be used by an external achievement class when the
797             * users unlocks an achievement.
798             * </p>
799             *
800             * @param userId the primary key of the user
801             * @param groupId the primary key of the group
802             */
803            @Override
804            public void incrementUserAchievementCounter(long userId, long groupId)
805                    throws PortalException {
806    
807                    User user = userPersistence.findByPrimaryKey(userId);
808    
809                    SocialActivityCounter activityCounter = addActivityCounter(
810                            groupId, user, new SocialActivityImpl(),
811                            _userAchievementsActivityCounterDefinition);
812    
813                    incrementActivityCounter(
814                            activityCounter, _userAchievementsActivityCounterDefinition);
815            }
816    
817            protected SocialActivityCounter addActivityCounter(
818                            final long groupId, final User user, final SocialActivity activity,
819                            final SocialActivityCounterDefinition activityCounterDefinition)
820                    throws PortalException {
821    
822                    int ownerType = activityCounterDefinition.getOwnerType();
823    
824                    long classNameId = getClassNameId(activity.getAssetEntry(), ownerType);
825                    long classPK = getClassPK(user, activity.getAssetEntry(), ownerType);
826    
827                    SocialActivityCounter activityCounter = fetchLatestActivityCounter(
828                            groupId, classNameId, classPK, activityCounterDefinition.getName(),
829                            ownerType);
830    
831                    if (activityCounter == null) {
832                            activityCounter = lockProtectedAddActivityCounter(
833                                    groupId, classNameId, classPK,
834                                    activityCounterDefinition.getName(), ownerType, 0, 0,
835                                    activityCounterDefinition.getPeriodLength());
836                    }
837                    else if (!activityCounter.isActivePeriod(
838                                            activityCounterDefinition.getPeriodLength())) {
839    
840                            activityCounter = lockProtectedAddActivityCounter(
841                                    groupId, classNameId, classPK,
842                                    activityCounterDefinition.getName(), ownerType,
843                                    activityCounter.getTotalValue(),
844                                    activityCounter.getActivityCounterId(),
845                                    activityCounterDefinition.getPeriodLength());
846                    }
847    
848                    if (activityCounterDefinition.getLimitValue() > 0) {
849                            SocialActivityLimit activityLimit =
850                                    socialActivityLimitPersistence.fetchByG_U_C_C_A_A(
851                                            groupId, user.getUserId(), activity.getClassNameId(),
852                                            getLimitClassPK(activity, activityCounterDefinition),
853                                            activity.getType(), activityCounterDefinition.getName());
854    
855                            if (activityLimit == null) {
856                                    lockProtectedGetActivityLimit(
857                                            groupId, user, activity, activityCounterDefinition);
858                            }
859                    }
860    
861                    return activityCounter;
862            }
863    
864            protected SocialActivityCounter addAssetActivitiesCounter(
865                            SocialActivity activity)
866                    throws PortalException {
867    
868                    User user = userPersistence.findByPrimaryKey(activity.getUserId());
869    
870                    return addActivityCounter(
871                            activity.getGroupId(), user, activity,
872                            _assetActivitiesActivityCounterDefinition);
873            }
874    
875            protected SocialActivityCounter addUserActivitiesCounter(
876                            SocialActivity activity)
877                    throws PortalException {
878    
879                    User user = userPersistence.findByPrimaryKey(activity.getUserId());
880    
881                    return addActivityCounter(
882                            activity.getGroupId(), user, activity,
883                            _userActivitiesActivityCounterDefinition);
884            }
885    
886            protected void adjustUserContribution(AssetEntry assetEntry, boolean enable)
887                    throws PortalException {
888    
889                    if (assetEntry == null) {
890                            return;
891                    }
892    
893                    SocialActivityCounter latestPopularityActivityCounter =
894                            fetchLatestActivityCounter(
895                                    assetEntry.getGroupId(), assetEntry.getClassNameId(),
896                                    assetEntry.getClassPK(),
897                                    SocialActivityCounterConstants.NAME_POPULARITY,
898                                    SocialActivityCounterConstants.TYPE_ASSET);
899    
900                    if ((latestPopularityActivityCounter == null) ||
901                            (enable && latestPopularityActivityCounter.isActive()) ||
902                            (!enable && !latestPopularityActivityCounter.isActive())) {
903    
904                            return;
905                    }
906    
907                    int factor = -1;
908    
909                    if (enable) {
910                            factor = 1;
911                    }
912    
913                    SocialActivityCounter latestContributionActivityCounter =
914                            fetchLatestActivityCounter(
915                                    assetEntry.getGroupId(),
916                                    classNameLocalService.getClassNameId(User.class.getName()),
917                                    assetEntry.getUserId(),
918                                    SocialActivityCounterConstants.NAME_CONTRIBUTION,
919                                    SocialActivityCounterConstants.TYPE_CREATOR);
920    
921                    if (latestContributionActivityCounter == null) {
922                            return;
923                    }
924    
925                    int startPeriod = SocialCounterPeriodUtil.getStartPeriod();
926    
927                    if (latestContributionActivityCounter.getStartPeriod() != startPeriod) {
928                            latestContributionActivityCounter = addActivityCounter(
929                                    latestContributionActivityCounter.getGroupId(),
930                                    latestContributionActivityCounter.getClassNameId(),
931                                    latestContributionActivityCounter.getClassPK(),
932                                    latestContributionActivityCounter.getName(),
933                                    latestContributionActivityCounter.getOwnerType(),
934                                    latestContributionActivityCounter.getTotalValue(),
935                                    latestContributionActivityCounter.getActivityCounterId(),
936                                    SocialActivityCounterConstants.PERIOD_LENGTH_SYSTEM);
937                    }
938    
939                    if (latestPopularityActivityCounter.getStartPeriod() == startPeriod) {
940                            latestContributionActivityCounter.setCurrentValue(
941                                    latestContributionActivityCounter.getCurrentValue() +
942                                            (latestPopularityActivityCounter.getCurrentValue() *
943                                                    factor));
944                    }
945    
946                    latestContributionActivityCounter.setTotalValue(
947                            latestContributionActivityCounter.getTotalValue() +
948                                    (latestPopularityActivityCounter.getTotalValue() * factor));
949    
950                    socialActivityCounterPersistence.update(
951                            latestContributionActivityCounter);
952            }
953    
954            protected boolean checkActivityLimit(
955                            User user, SocialActivity activity,
956                            SocialActivityCounterDefinition activityCounterDefinition)
957                    throws PortalException {
958    
959                    if (activityCounterDefinition.getLimitValue() == 0) {
960                            return true;
961                    }
962    
963                    long classPK = activity.getClassPK();
964    
965                    String name = activityCounterDefinition.getName();
966    
967                    if (name.equals(SocialActivityCounterConstants.NAME_PARTICIPATION)) {
968                            classPK = 0;
969                    }
970    
971                    SocialActivityLimit activityLimit =
972                            socialActivityLimitPersistence.findByG_U_C_C_A_A(
973                                    activity.getGroupId(), user.getUserId(),
974                                    activity.getClassNameId(), classPK, activity.getType(), name);
975    
976                    int count = activityLimit.getCount(
977                            activityCounterDefinition.getLimitPeriod());
978    
979                    if (count < activityCounterDefinition.getLimitValue()) {
980                            activityLimit.setCount(
981                                    activityCounterDefinition.getLimitPeriod(), count + 1);
982    
983                            socialActivityLimitPersistence.update(activityLimit);
984    
985                            return true;
986                    }
987    
988                    return false;
989            }
990    
991            protected void clearFinderCache() {
992                    PortalCache<String, SocialActivityCounter> portalCache =
993                            MultiVMPoolUtil.getPortalCache(
994                                    SocialActivityCounterFinder.class.getName());
995    
996                    portalCache.removeAll();
997            }
998    
999            protected long getClassNameId(AssetEntry assetEntry, int ownerType) {
1000                    if (ownerType == SocialActivityCounterConstants.TYPE_ASSET) {
1001                            return assetEntry.getClassNameId();
1002                    }
1003    
1004                    return classNameLocalService.getClassNameId(User.class.getName());
1005            }
1006    
1007            protected long getClassPK(User user, AssetEntry assetEntry, int ownerType) {
1008                    if (ownerType == SocialActivityCounterConstants.TYPE_ACTOR) {
1009                            return user.getUserId();
1010                    }
1011    
1012                    if (ownerType == SocialActivityCounterConstants.TYPE_ASSET) {
1013                            return assetEntry.getClassPK();
1014                    }
1015    
1016                    return assetEntry.getUserId();
1017            }
1018    
1019            protected long getLimitClassPK(
1020                    SocialActivity activity,
1021                    SocialActivityCounterDefinition activityCounterDefinition) {
1022    
1023                    String name = activityCounterDefinition.getName();
1024    
1025                    if (name.equals(SocialActivityCounterConstants.NAME_PARTICIPATION)) {
1026                            return 0;
1027                    }
1028    
1029                    return activity.getClassPK();
1030            }
1031    
1032            protected void incrementActivityCounter(
1033                    SocialActivityCounter activityCounter,
1034                    SocialActivityCounterDefinition activityCounterDefinition) {
1035    
1036                    activityCounter.setCurrentValue(
1037                            activityCounter.getCurrentValue() +
1038                                    activityCounterDefinition.getIncrement());
1039                    activityCounter.setTotalValue(
1040                            activityCounter.getTotalValue() +
1041                                    activityCounterDefinition.getIncrement());
1042    
1043                    socialActivityCounterPersistence.update(activityCounter);
1044    
1045                    socialActivityCounterPersistence.clearCache(activityCounter);
1046            }
1047    
1048            protected boolean isAddActivityCounter(
1049                    User user, User assetEntryUser, AssetEntry assetEntry,
1050                    SocialActivityCounterDefinition activityCounterDefinition) {
1051    
1052                    if ((user.isDefaultUser() || !user.isActive()) &&
1053                            (activityCounterDefinition.getOwnerType() !=
1054                                    SocialActivityCounterConstants.TYPE_ASSET)) {
1055    
1056                            return false;
1057                    }
1058    
1059                    if ((assetEntryUser.isDefaultUser() || !assetEntryUser.isActive()) &&
1060                            (activityCounterDefinition.getOwnerType() !=
1061                                    SocialActivityCounterConstants.TYPE_ACTOR)) {
1062    
1063                            return false;
1064                    }
1065    
1066                    if (!activityCounterDefinition.isEnabled() ||
1067                            (activityCounterDefinition.getIncrement() == 0)) {
1068    
1069                            return false;
1070                    }
1071    
1072                    String name = activityCounterDefinition.getName();
1073    
1074                    if ((user.getUserId() == assetEntryUser.getUserId()) &&
1075                            (name.equals(SocialActivityCounterConstants.NAME_CONTRIBUTION) ||
1076                             name.equals(SocialActivityCounterConstants.NAME_POPULARITY))) {
1077    
1078                            return false;
1079                    }
1080    
1081                    if ((activityCounterDefinition.getOwnerType() ==
1082                                    SocialActivityCounterConstants.TYPE_ASSET) &&
1083                            !assetEntry.isVisible()) {
1084    
1085                            return false;
1086                    }
1087    
1088                    return true;
1089            }
1090    
1091            protected SocialActivityCounter lockProtectedAddActivityCounter(
1092                            final long groupId, final long classNameId, final long classPK,
1093                            final String name, final int ownerType, final int totalValue,
1094                            final long previousActivityCounterId, final int periodLength)
1095                    throws PortalException {
1096    
1097                    String lockKey = StringUtil.merge(
1098                            new Object[] {groupId, classNameId, classPK, name, ownerType},
1099                            StringPool.POUND);
1100    
1101                    LockProtectedAction<SocialActivityCounter> lockProtectedAction =
1102                            new LockProtectedAction<SocialActivityCounter>(
1103                                    SocialActivityCounter.class, lockKey,
1104                                    PropsValues.SOCIAL_ACTIVITY_LOCK_TIMEOUT,
1105                                    PropsValues.SOCIAL_ACTIVITY_LOCK_RETRY_DELAY) {
1106    
1107                                    @Override
1108                                    protected SocialActivityCounter performProtectedAction()
1109                                            throws PortalException {
1110    
1111                                            SocialActivityCounter activityCounter =
1112                                                    socialActivityCounterLocalService.addActivityCounter(
1113                                                            groupId, classNameId, classPK, name, ownerType,
1114                                                            totalValue, previousActivityCounterId,
1115                                                            periodLength);
1116    
1117                                            return activityCounter;
1118                                    }
1119    
1120                            };
1121    
1122                    lockProtectedAction.performAction();
1123    
1124                    return lockProtectedAction.getReturnValue();
1125            }
1126    
1127            protected void lockProtectedGetActivityLimit(
1128                            final long groupId, final User user, final SocialActivity activity,
1129                            final SocialActivityCounterDefinition activityCounterDefinition)
1130                    throws PortalException {
1131    
1132                    final long classPK = getLimitClassPK(
1133                            activity, activityCounterDefinition);
1134    
1135                    String lockKey = StringUtil.merge(
1136                            new Object[] {
1137                                    groupId, user.getUserId(), activity.getClassNameId(), classPK,
1138                                    activity.getType(), activityCounterDefinition.getName()
1139                            },
1140                            StringPool.POUND);
1141    
1142                    LockProtectedAction<SocialActivityLimit> lockProtectedAction =
1143                            new LockProtectedAction<SocialActivityLimit>(
1144                                    SocialActivityLimit.class, lockKey,
1145                                    PropsValues.SOCIAL_ACTIVITY_LOCK_TIMEOUT,
1146                                    PropsValues.SOCIAL_ACTIVITY_LOCK_RETRY_DELAY) {
1147    
1148                                    @Override
1149                                    protected SocialActivityLimit performProtectedAction()
1150                                            throws PortalException {
1151    
1152                                            SocialActivityLimit activityLimit =
1153                                                    socialActivityLimitPersistence.fetchByG_U_C_C_A_A(
1154                                                            groupId, user.getUserId(),
1155                                                            activity.getClassNameId(), classPK,
1156                                                            activity.getType(),
1157                                                            activityCounterDefinition.getName());
1158    
1159                                            if (activityLimit == null) {
1160                                                    activityLimit =
1161                                                            socialActivityLimitLocalService.addActivityLimit(
1162                                                                    user.getUserId(), activity.getGroupId(),
1163                                                                    activity.getClassNameId(), classPK,
1164                                                                    activity.getType(),
1165                                                                    activityCounterDefinition.getName(),
1166                                                                    activityCounterDefinition.getLimitPeriod());
1167                                            }
1168    
1169                                            return activityLimit;
1170                                    }
1171    
1172                            };
1173    
1174                    lockProtectedAction.performAction();
1175    
1176                    socialActivityLimitPersistence.cacheResult(
1177                            lockProtectedAction.getReturnValue());
1178            }
1179    
1180            private final SocialActivityCounterDefinition
1181                    _assetActivitiesActivityCounterDefinition =
1182                            new SocialActivityCounterDefinition(
1183                                    SocialActivityCounterConstants.NAME_ASSET_ACTIVITIES,
1184                                    SocialActivityCounterConstants.TYPE_ASSET);
1185            private final SocialActivityCounterDefinition
1186                    _userAchievementsActivityCounterDefinition =
1187                            new SocialActivityCounterDefinition(
1188                                    SocialActivityCounterConstants.NAME_USER_ACHIEVEMENTS,
1189                                    SocialActivityCounterConstants.TYPE_ACTOR);
1190            private final SocialActivityCounterDefinition
1191                    _userActivitiesActivityCounterDefinition =
1192                            new SocialActivityCounterDefinition(
1193                                    SocialActivityCounterConstants.NAME_USER_ACTIVITIES,
1194                                    SocialActivityCounterConstants.TYPE_ACTOR);
1195    
1196    }