com.edgenius.wiki.service.impl.ActivityLogServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.edgenius.wiki.service.impl.ActivityLogServiceImpl.java

Source

/* 
 * =============================================================
 * Copyright (C) 2007-2011 Edgenius (http://www.edgenius.com)
 * =============================================================
 * License Information: http://www.edgenius.com/licensing/edgenius/2.0/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2.0
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * http://www.gnu.org/licenses/gpl.txt
 *  
 * ****************************************************************
 */
package com.edgenius.wiki.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.edgenius.core.Constants.REGISTER_METHOD;
import com.edgenius.core.SecurityValues;
import com.edgenius.core.model.User;
import com.edgenius.core.repository.FileNode;
import com.edgenius.core.service.MessageService;
import com.edgenius.core.service.UserReadingService;
import com.edgenius.core.util.AuditLogger;
import com.edgenius.core.util.DateUtil;
import com.edgenius.wiki.ActivityType;
import com.edgenius.wiki.ActivityType.SubType;
import com.edgenius.wiki.dao.ActivityLogDAO;
import com.edgenius.wiki.gwt.client.model.RenderMarkupModel;
import com.edgenius.wiki.gwt.client.model.RenderPiece;
import com.edgenius.wiki.gwt.client.model.TextModel;
import com.edgenius.wiki.gwt.client.server.utils.GwtUtils;
import com.edgenius.wiki.gwt.client.server.utils.SharedConstants;
import com.edgenius.wiki.gwt.server.UserUtil;
import com.edgenius.wiki.model.ActivityLog;
import com.edgenius.wiki.model.Page;
import com.edgenius.wiki.model.PageComment;
import com.edgenius.wiki.model.Space;
import com.edgenius.wiki.security.service.SecurityService;
import com.edgenius.wiki.service.ActivityLogService;
import com.edgenius.wiki.util.WikiUtil;

/**
 * @author Dapeng.Ni
 */
@Transactional
public class ActivityLogServiceImpl implements ActivityLogService {
    private static final Logger log = LoggerFactory.getLogger(ActivityLogServiceImpl.class);

    @Autowired
    private ActivityLogDAO activityLogDAO;

    @Autowired
    private MessageService messageService;
    @Autowired
    private SecurityService securityService;
    @Autowired
    private UserReadingService userReadingService;

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // View 
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    @Transactional(readOnly = true)
    public List<ActivityLog> getUserActivities(int start, int count, User user, User viewer) {

        List<ActivityLog> activities = activityLogDAO.getByUser(start, count, user);
        if (activities == null || activities.size() == 0) {
            return new ArrayList<ActivityLog>();
        }

        return buildActivitiesMessage(viewer, activities);
    }

    @Transactional(readOnly = true)
    public List<ActivityLog> getActivities(int start, int count, User viewer) {

        List<ActivityLog> logs = new ArrayList<ActivityLog>();

        int retry = 0;
        do {
            //get double size of expected count, to minor database read.
            List<ActivityLog> activities = activityLogDAO.getByCount(start, count * 2);
            if (activities == null || activities.size() == 0) {
                break;
            }

            //here, some log messages maybe filter out for permission reason
            logs.addAll(buildActivitiesMessage(viewer, activities));
            //already get all logs in database
            if (count <= 0)
                break;

            //try to read more log
            start = start + (count * 2);
            retry++;
        } while (logs.size() < count && retry < 5);

        if (logs.size() > count) {
            logs = logs.subList(0, count);
        }
        return logs;
    }

    /**
     * @param viewer
     * @param msgs
     * @param activities
     * @return 
     */
    private List<ActivityLog> buildActivitiesMessage(User viewer, List<ActivityLog> activities) {
        String spaceUname;
        List<ActivityLog> retActivities = new ArrayList<ActivityLog>();
        for (ActivityLog activity : activities) {
            spaceUname = null;
            if (activity.getType() == ActivityType.Type.PAGE_EVENT.getCode()
                    || activity.getType() == ActivityType.Type.COMMENT_EVENT.getCode()
                    || activity.getType() == ActivityType.Type.ATTACHMENT_EVENT.getCode()) {
                spaceUname = activity.getExtroInfo();
            } else if (activity.getType() == ActivityType.Type.SPACE_EVENT.getCode()) {
                spaceUname = activity.getSrcResourceName();
            }
            //for activities, only private space stuff(space, page, comment) needs to be check if hide or not.
            if (spaceUname != null && securityService.isPrivateSpace(spaceUname)) {
                if (!securityService.isAllowSpaceReading(spaceUname, viewer)) {
                    log.debug("Activity {} is private and not allow to view on user {}", activity.getType(),
                            viewer);
                    continue;
                }
            }
            String msg = null;
            if (activity.getType() == ActivityType.Type.PAGE_EVENT.getCode()) {
                if (activity.getSubType() == ActivityType.SubType.COPY.getCode()
                        || activity.getSubType() == ActivityType.SubType.MOVE.getCode()) {
                    //moves page {1} to {2} in space {3}  #title, new title and  space, time
                    msg = messageService.getMessage("activity.page." + SubType.valueOfCode(activity.getSubType()),
                            new Object[] {
                                    WikiUtil.getPageRelativeTokenLink(activity.getExtroInfo(),
                                            activity.getSrcResourceName(), null),
                                    WikiUtil.getPageRelativeTokenLink(activity.getExtroInfo(),
                                            activity.getTgtResourceName(), null),
                                    activity.getExtroInfo(), DateUtil.toDisplayDateWithPrep(viewer,
                                            activity.getCreatedDate(), messageService) });

                } else if (activity.getSubType() == ActivityType.SubType.REVERT.getCode()) {
                    //Reverts page {1} from history version {2} {3}  # title, version, time
                    msg = messageService.getMessage("activity.page." + SubType.valueOfCode(activity.getSubType()),
                            new Object[] {
                                    WikiUtil.getPageRelativeTokenLink(activity.getExtroInfo(),
                                            activity.getSrcResourceName(), null),
                                    activity.getTgtResourceName(), DateUtil.toDisplayDateWithPrep(viewer,
                                            activity.getCreatedDate(), messageService) });

                } else {
                    //ActivityType.SubType.CREATE, UPDATE, RESTORE, PERMANENT_DELETE or DELETE
                    //Creates new page {1} {2}  #title, time
                    msg = messageService.getMessage("activity.page." + SubType.valueOfCode(activity.getSubType()),
                            new Object[] {
                                    WikiUtil.getPageRelativeTokenLink(activity.getExtroInfo(),
                                            activity.getSrcResourceName(), null),
                                    DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                            messageService) });

                }
            } else if (activity.getType() == ActivityType.Type.COMMENT_EVENT.getCode()) {
                //ActivityType.SubType.CREATE, UPDATE
                //Posts  comment on page {1} {2} #  page title, time
                msg = messageService.getMessage("activity.comment." + SubType.valueOfCode(activity.getSubType()),
                        new Object[] {
                                WikiUtil.getPageRelativeTokenLink(activity.getExtroInfo(),
                                        activity.getTgtResourceName(), null),
                                DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                        messageService) });

            } else if (activity.getType() == ActivityType.Type.ATTACHMENT_EVENT.getCode()) {
                //ActivityType.SubType.CREATE, DELETE
                //uploads attachment {1} to page {2} {3} #  attachment title, page title, time
                msg = messageService.getMessage("activity.attachment." + SubType.valueOfCode(activity.getSubType()),
                        new Object[] { activity.getSrcResourceName(),
                                WikiUtil.getPageRelativeTokenLink(activity.getExtroInfo(),
                                        activity.getTgtResourceName(), null),
                                DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                        messageService) });

            } else if (activity.getType() == ActivityType.Type.SPACE_EVENT.getCode()) {
                //ActivityType.SubType.CREATE, RESTORE,PERMANENT_DELETE,DELETE
                //creates new space {0} {1} # space, time
                //restored space {0} {1}  #space,  time
                //puts space {0} to trash bin {1}  # space, time
                //Deleted space {0} permanently {1}  #space, time
                msg = messageService.getMessage("activity.space." + SubType.valueOfCode(activity.getSubType()),
                        new Object[] {
                                //this space is delete by Quartz job, so mark the user as "System" rather than anonymous.
                                //extra info is space name.
                                WikiUtil.getSpaceRelativeTokenLink(activity.getSrcResourceName(),
                                        activity.getExtroInfo()),
                                DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                        messageService) });

            } else if (activity.getType() == ActivityType.Type.USER_EVENT.getCode()) {
                if (activity.getSubType() == ActivityType.SubType.CREATE.getCode()) {
                    //Sign-up {0}.

                    String status = StringUtils.trimToNull(activity.getExtroInfo());
                    if (status == null) {
                        msg = messageService.getMessage(
                                "activity.user.approved." + SubType.valueOfCode(activity.getSubType()),
                                new Object[] { DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                        messageService) });
                    } else if (REGISTER_METHOD.approval.name().equals(status)) {
                        msg = messageService.getMessage(
                                "activity.user.wait." + SubType.valueOfCode(activity.getSubType()),
                                new Object[] { DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                        messageService) });
                    } else { //REGISTER_METHOD.signup.name().equals(status)
                        msg = messageService.getMessage(
                                "activity.user." + SubType.valueOfCode(activity.getSubType()),
                                new Object[] { DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                        messageService) });
                    }

                } else if (activity.getSubType() == ActivityType.SubType.UPDATE.getCode()) {
                    //Updates status {1} {2}.
                    msg = messageService.getMessage("activity.user." + SubType.valueOfCode(activity.getSubType()),
                            new Object[] { activity.getExtroInfo(), DateUtil.toDisplayDateWithPrep(viewer,
                                    activity.getCreatedDate(), messageService) });

                } else if (activity.getSubType() == ActivityType.SubType.FOLLOW.getCode()
                        || activity.getSubType() == ActivityType.SubType.UNFOLLOW.getCode()) {
                    //{0} follows {1} {2}.
                    msg = messageService.getMessage("activity.user." + SubType.valueOfCode(activity.getSubType()),
                            new Object[] {
                                    //this space is delete by Quartz job, so mark the user as "System" rather than anonymous.
                                    getUserLink(userReadingService.getUserByName(activity.getSrcResourceName())),
                                    getUserLink(userReadingService.getUserByName(activity.getTgtResourceName())),
                                    DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                            messageService) });

                }
            } else if (activity.getType() == ActivityType.Type.SYSTEM_EVENT.getCode()) {
                if (activity.getSubType() == ActivityType.SubType.VERSION_PING.getCode()) {
                    msg = messageService.getMessage("activity.system." + SubType.valueOfCode(activity.getSubType()),
                            new Object[] { activity.getExtroInfo() });

                } else if (activity.getSubType() == ActivityType.SubType.REDEPLOY_SHELL.getCode()) {
                    msg = messageService.getMessage("activity.system." + SubType.valueOfCode(activity.getSubType()),
                            new Object[] { DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                    messageService) });

                } else if (activity.getSubType() == ActivityType.SubType.REBUILD_INDEX.getCode()) {
                    if (activity.getStatus() > 0) {
                        msg = messageService.getMessage(
                                "activity.system." + SubType.valueOfCode(activity.getSubType()),
                                new Object[] { DateUtil.toDisplayDateWithPrep(viewer, activity.getCreatedDate(),
                                        messageService) });
                    }
                }
            }

            if (msg != null) {
                activity = (ActivityLog) activity.clone();
                //anonymous
                if (activity.getCreator() == null)
                    activity.setCreator(userReadingService.getUserByName(null));
                //system
                if ((activity.getType() == ActivityType.Type.SPACE_EVENT.getCode()
                        && activity.getSubType() == ActivityType.SubType.PERMANENT_DELETE.getCode())
                        || activity.getType() == ActivityType.Type.SYSTEM_EVENT.getCode()) {
                    activity.setCreator(null);
                }

                activity.setMessage(msg);
                retActivities.add(activity);
            } else {
                AuditLogger.error("System has unknown activitiy can not be render: " + activity);
            }
        }

        return retActivities;
    }

    /**
     * @param activity
     * @return
     */
    private String getUserLink(User user) {
        return user.getFullname();
    }

    // Page log
    public void logPageSaved(Page page) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.PAGE_EVENT.getCode());
            activity.setSubType(page.getVersion() == 1 ? ActivityType.SubType.CREATE.getCode()
                    : ActivityType.SubType.UPDATE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setSrcResourceName(page.getTitle());

            //put spaceUname as extro info so that easy to do security check at retrieving
            activity.setExtroInfo(page.getSpace().getUnixName());

            activity.setCreator(page.getModifier());
            activity.setCreatedDate(page.getModifiedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for save page", e);
        }
    }

    public void logPageRestored(Page page) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.PAGE_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.RESTORE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setSrcResourceName(page.getTitle());

            //put spaceUname as extro info so that easy to do security check at retrieving
            activity.setExtroInfo(page.getSpace().getUnixName());

            activity.setCreator(page.getModifier());
            activity.setCreatedDate(page.getModifiedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for save page", e);
        }
    }

    public void logPageRemoved(Page page, boolean permanent, User activityRequester) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.PAGE_EVENT.getCode());
            activity.setSubType(permanent ? ActivityType.SubType.PERMANENT_DELETE.getCode()
                    : ActivityType.SubType.DELETE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setSrcResourceName(page.getTitle());

            //put spaceUname as extro info so that easy to do security check at retrieving
            activity.setExtroInfo(page.getSpace().getUnixName());

            activity.setCreator(activityRequester);
            activity.setCreatedDate(new Date());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for remove page", e);
        }
    }

    public void logPageCopied(Page src, Page tgt) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.PAGE_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.COPY.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setSrcResourceName(src.getTitle());
            activity.setTgtResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setTgtResourceName(tgt.getTitle());

            //put spaceUname as extro info so that easy to do security check at retrieving
            activity.setExtroInfo(src.getSpace().getUnixName());

            activity.setCreator(tgt.getModifier());
            activity.setCreatedDate(tgt.getModifiedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for copy page", e);
        }
    }

    public void logPageMoved(Page src, Page tgt) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.PAGE_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.MOVE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setSrcResourceName(src.getTitle());
            activity.setTgtResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setTgtResourceName(tgt.getTitle());

            //put spaceUname as extro info so that easy to do security check at retrieving
            activity.setExtroInfo(src.getSpace().getUnixName());

            activity.setCreator(tgt.getModifier());
            activity.setCreatedDate(tgt.getModifiedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for move page", e);
        }
    }

    public void logPageReverted(Page page, int version) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.PAGE_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.REVERT.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.PAGE.ordinal());
            activity.setSrcResourceName(page.getTitle());
            activity.setTgtResourceName(String.valueOf(version));

            //put spaceUname as extro info so that easy to do security check at retrieving
            activity.setExtroInfo(page.getSpace().getUnixName());

            activity.setCreator(page.getModifier());
            activity.setCreatedDate(page.getModifiedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for revert page", e);
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Comment log
    public void logComment(PageComment comment) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.COMMENT_EVENT.getCode());
            activity.setSubType(comment.getParent() == null ? ActivityType.SubType.CREATE.getCode()
                    : ActivityType.SubType.UPDATE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.COMMENT.ordinal());
            activity.setSrcResourceName(comment.getUid().toString());
            activity.setTgtResourceName(comment.getPage().getTitle());

            //put spaceUname as extro info so that easy to do security check at retrieving
            activity.setExtroInfo(comment.getPage().getSpace().getUnixName());

            activity.setCreator(comment.getModifier());
            activity.setCreatedDate(comment.getModifiedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for comment", e);
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Attachment log
    public void logAttachmentUploaded(String spaceUname, String pageTitle, User creator,
            List<FileNode> attachment) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.ATTACHMENT_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.CREATE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.ATTACHMENT.ordinal());
            //put spaceUname as extro info so that easy to do security check at retrieving
            StringBuffer files = new StringBuffer();
            for (FileNode fileNode : attachment) {
                files.append(fileNode.getFilename()).append(",");
            }
            if (files.length() > 0) {
                //delete last comma
                files.deleteCharAt(files.length() - 1);
            }

            activity.setSrcResourceName(files.toString());
            activity.setTgtResourceName(pageTitle);

            activity.setExtroInfo(spaceUname);

            activity.setCreator(creator);
            activity.setCreatedDate(new Date());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for comment", e);
        }
    }

    public void logAttachmentRemoved(String spaceUname, String pageTitle, User creator, FileNode attachment) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.ATTACHMENT_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.DELETE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.ATTACHMENT.ordinal());
            activity.setSrcResourceName(attachment.getFilename().toString());
            activity.setTgtResourceName(pageTitle);

            activity.setExtroInfo(spaceUname);

            activity.setCreator(creator);
            activity.setCreatedDate(new Date());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for comment", e);
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Space log
    public void logSpaceCreated(Space space) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.SPACE_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.CREATE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.SPACE.ordinal());
            activity.setSrcResourceName(space.getUnixName());
            activity.setExtroInfo(space.getName());

            activity.setCreator(space.getCreator());
            activity.setCreatedDate(space.getCreatedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for create space", e);
        }
    }

    public void logSpaceRemoved(Space space, User requestor, boolean permanent) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.SPACE_EVENT.getCode());
            activity.setSubType(permanent ? ActivityType.SubType.PERMANENT_DELETE.getCode()
                    : ActivityType.SubType.DELETE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.SPACE.ordinal());
            activity.setSrcResourceName(space.getUnixName());
            activity.setExtroInfo(space.getName());

            activity.setCreator(requestor);
            activity.setCreatedDate(space.getModifiedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for remove space", e);
        }
    }

    public void logSpaceRestored(String spaceUname, String name, User requestor) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.SPACE_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.RESTORE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.SPACE.ordinal());
            activity.setSrcResourceName(spaceUname);
            activity.setExtroInfo(name);

            activity.setCreator(requestor);
            activity.setCreatedDate(new Date());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for undo remove space", e);
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // User log
    public void logUserSignup(User user, REGISTER_METHOD registerStatus) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.USER_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.CREATE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.USER.ordinal());
            activity.setSrcResourceName(user.getUsername());

            //put user enable status here, this will check if the signup needs to be approved.
            activity.setExtroInfo(registerStatus == null ? null : registerStatus.name());

            activity.setCreator(user);
            activity.setCreatedDate(user.getCreatedDate());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for user sign-up", e);
        }
    }

    public void logUserStatusUpdate(User user) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.USER_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.UPDATE.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.USER.ordinal());
            activity.setSrcResourceName(user.getUsername());
            activity.setExtroInfo(StringUtils.abbreviate(user.getSetting().getStatus(), 120));

            activity.setCreator(user);
            activity.setCreatedDate(new Date());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for user update status", e);
        }
    }

    public void logUserFollowing(User user, User follower) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.USER_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.FOLLOW.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.USER.ordinal());
            activity.setSrcResourceName(user.getUsername());
            activity.setTgtResourceType(SecurityValues.RESOURCE_TYPES.USER.ordinal());
            activity.setTgtResourceName(follower.getUsername());

            activity.setCreator(user);
            activity.setCreatedDate(new Date());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for following", e);
        }
    }

    public void logUserUnFollowing(User user, User follower) {
        try {
            ActivityLog activity = new ActivityLog();
            activity.setType(ActivityType.Type.USER_EVENT.getCode());
            activity.setSubType(ActivityType.SubType.UNFOLLOW.getCode());
            activity.setSrcResourceType(SecurityValues.RESOURCE_TYPES.USER.ordinal());
            activity.setSrcResourceName(user.getUsername());
            activity.setTgtResourceType(SecurityValues.RESOURCE_TYPES.USER.ordinal());
            activity.setTgtResourceName(follower.getUsername());

            activity.setCreator(user);
            activity.setCreatedDate(new Date());
            this.save(activity);
        } catch (Exception e) {
            log.warn("Activity log failed for unfollowing", e);
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // General log method
    public void save(ActivityLog activity) {
        activityLogDAO.saveOrUpdate(activity);
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Others...
    public List<ActivityLog> getByTarget(int typeCode, int subTypeCode, int tgtResourceType,
            String tgtResourceName) {
        return activityLogDAO.getByTarget(typeCode, subTypeCode, tgtResourceType, tgtResourceName);
    }

    public void purgeActivityLog(int days) {
        activityLogDAO.removeOldByDays(days);

    }

    public List<ActivityLog> getBySource(int typeCode, int subTypeCode, int srcResourceType,
            String srcResourceName) {
        return activityLogDAO.getBySource(typeCode, subTypeCode, srcResourceType, srcResourceName);
    }

    /**
     * @param list
     * @return
     */
    public RenderMarkupModel renderActivities(List<ActivityLog> list) {
        ArrayList<RenderPiece> pieces = new ArrayList<RenderPiece>();
        if (list != null && list.size() > 0) {
            User user = null;
            boolean first = true; //as user==null is not enough to tell it is first line because log.getCreator() could be null 
            for (ActivityLog log : list) {
                if (first || (user != null && !user.equals(log.getCreator()))
                        || (user == null && log.getCreator() != null)) {
                    first = false;

                    pieces.add(new TextModel("<div class='widget-activity-user'>"));
                    user = log.getCreator();
                    if (user == null) {
                        //TODO: hardcode for "System"
                        //system user, no link on it
                        pieces.add(new TextModel(GwtUtils.getUserPortraitHTML(UserUtil.getPortraitUrl(null),
                                "System", SharedConstants.PORTRAIT_SIZE_SMALL)
                                + "<div class='username'>System</div>"));
                    } else {
                        //hover popup user
                        pieces.add(new TextModel(
                                GwtUtils.getUserPortraitHTML(UserUtil.getPortraitUrl(user.getPortrait()),
                                        user.getFullname(), SharedConstants.PORTRAIT_SIZE_SMALL)
                                        + "<div class='username'>"));

                        pieces.add(WikiUtil.createUserLinkModel(user));
                        pieces.add(new TextModel("</div>"));
                    }

                    pieces.add(new TextModel("</div>"));

                }
                pieces.add(new TextModel("<div class='widget-activity-text'>" + log.getMessage() + "</div>"));
            }
        }
        RenderMarkupModel model = new RenderMarkupModel();
        model.renderContent = pieces;
        return model;
    }
}