Package models

Source Code of models.Project

/**
* Yobi, Project Hosting SW
*
* Copyright 2012 NAVER Corp.
* http://yobi.io
*
* @Author Hwi Ahn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package models;

import com.avaje.ebean.Ebean;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.Page;
import controllers.UserApp;
import models.enumeration.ProjectScope;
import models.enumeration.RequestState;
import models.enumeration.ResourceType;
import models.enumeration.RoleType;
import models.resource.GlobalResource;
import models.resource.Resource;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.tmatesoft.svn.core.SVNException;
import play.data.validation.Constraints;
import play.db.ebean.Model;
import play.db.ebean.Transactional;
import playRepository.*;
import utils.FileUtil;
import utils.JodaDateUtil;
import validation.ExConstraints;

import javax.persistence.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.*;

@Entity
public class Project extends Model implements LabelOwner {
    private static final long serialVersionUID = 1L;
    public static final play.db.ebean.Model.Finder <Long, Project> find = new Finder<>(Long.class, Project.class);

    private static final int DRAFT_TIME_IN_MILLIS = 1000 * 60 * 60;

    @Id
    public Long id;

    @Constraints.Required
    @Constraints.Pattern("^[a-zA-Z0-9-_\\.]+$")
    @ExConstraints.Restricted({".", "..", ".git"})
    public String name;

    public String overview;
    public String vcs;
    public String siteurl;
    public String owner;

    public Date createdDate;

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
    public Set<Issue> issues;

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
    public List<ProjectUser> projectUser;

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
    public List<Posting> posts;

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
    public List<Milestone> milestones;

    /** Project Notification */
    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
    public List<UserProjectNotification> notifications;

    private long lastIssueNumber;

    private long lastPostingNumber;

    @ManyToMany
    public Set<Label> labels;

    @ManyToOne
    public Project originalProject;

    @OneToMany(mappedBy = "originalProject")
    public List<Project> forkingProjects;

    @OneToMany(mappedBy = "project")
    public Set<Assignee> assignees;

    public Date lastPushedDate;

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
    public List<PushedBranch> pushedBranches;

    @ManyToMany(mappedBy = "enrolledProjects")
    public List<User> enrolledUsers;

    @OneToMany(cascade = CascadeType.REMOVE)
    public List<CommitComment> codeComments;

    @OneToMany(cascade = CascadeType.REMOVE)
    public List<CommentThread> commentThreads;

    public Integer defaultReviewerCount = 1;

    public boolean isUsingReviewerCount;

    @ManyToOne
    public Organization organization;

    @Enumerated(EnumType.STRING)
    public ProjectScope projectScope;

    @OneToOne(mappedBy = "project", cascade = CascadeType.ALL)
    public ProjectMenuSetting menuSetting;

    /**
     * @see {@link User#SITE_MANAGER_ID}
     * @see {@link RoleType#SITEMANAGER}
     */
    public static Long create(Project newProject) {
        newProject.siteurl = "http://localhost:9000/" + newProject.name;
        newProject.createdDate = new Date();
        newProject.save();
        ProjectUser.assignRole(User.SITE_MANAGER_ID, newProject.id,
                RoleType.SITEMANAGER);
        return newProject.id;
    }

    public static Page<Project> findByName(String name, int pageSize,
                                           int pageNum) {
        return find.where().ilike("name", "%" + name + "%")
                .findPagingList(pageSize).getPage(pageNum);
    }

    public static Project findByOwnerAndProjectName(String loginId, String projectName) {
        return find.where().ieq("owner", loginId).ieq("name", projectName)
                .findUnique();
    }

    public static boolean exists(String loginId, String projectName) {
        int findRowCount = find.where().ieq("owner", loginId)
                .ieq("name", projectName).findRowCount();
        return (findRowCount != 0);
    }

    public static boolean projectNameChangeable(Long id, String userName,
                                                String projectName) {
        int findRowCount = find.where().ieq("name", projectName)
                .ieq("owner", userName).ne("id", id).findRowCount();
        return (findRowCount == 0);
    }

    /**
     * @see {@link RoleType#MANAGER}
     */
    public static boolean isOnlyManager(Long userId) {
        List<Project> projects = find.select("id").select("name").where()
                .eq("projectUser.user.id", userId)
                .eq("projectUser.role.id", RoleType.MANAGER.roleType())
                .findList();

        for (Project project : projects) {
            if (ProjectUser.checkOneMangerPerOneProject(userId, project.id)) {
                return true;
            }
        }
        return false;
    }

    public static List<Project> findProjectsByMember(Long userId) {
        return find.where().eq("projectUser.user.id", userId).findList();
    }

    public static List<Project> findProjectsJustMemberAndNotOwner(User user) {
        return findProjectsJustMemberAndNotOwner(user, null);
    }

    public static List<Project> findProjectsJustMemberAndNotOwner(User user, String orderString) {
        ExpressionList<Project> el = find.where()
                .eq("projectUser.user.id", user.id)
                .ne("projectUser.role.id", RoleType.SITEMANAGER.roleType())
                .ne("owner", user.loginId);
        if (StringUtils.isNotBlank(orderString)) {
            el.orderBy(orderString);
        }
        return el.findList();
    }


    public static List<Project> findProjectsByMemberWithFilter(Long userId, String orderString) {
        List<Project> userProjectList = find.where().eq("projectUser.user.id", userId).findList();
        if( orderString == null ){
            return userProjectList;
        }

        return Ebean.filter(Project.class).sort(orderString).filter(userProjectList);
    }

    public static List<Project> findProjectsCreatedByUser(String loginId, String orderString) {
        List<Project> userProjectList = find.where().eq("owner", loginId).findList();
        if( orderString == null ){
            return userProjectList;
        }

        return Ebean.filter(Project.class).sort(orderString).filter(userProjectList);
    }

    public Date lastUpdateDate() {
        try {
            PlayRepository repository = RepositoryService.getRepository(this);
            List<String> branches = repository.getBranches();
            if (!branches.isEmpty() && repository instanceof GitRepository) {
                GitRepository gitRepo = new GitRepository(owner, name);
                List<Commit> history = gitRepo.getHistory(0, 2, "HEAD", null);
                return history.get(0).getAuthorDate();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoHeadException e) {
            e.printStackTrace();
        } catch (GitAPIException e) {
            e.printStackTrace();
        } catch (UnsupportedOperationException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }
        return this.createdDate;
    }

    public String defaultBranch() {
        try {
            return RepositoryService.getRepository(this).getDefaultBranch();
        } catch (Exception ignored) {
        }
        return "HEAD";
    }

    public Duration ago() {
        return JodaDateUtil.ago(lastUpdateDate());
    }

    public Duration lastPushedDateAgo(){
        if( this.lastPushedDate == null){
            return null;
        }
        return JodaDateUtil.ago(this.lastPushedDate);
    }

    public String readme() {
        try {
            byte[] bytes = RepositoryService.getRepository(this)
                    .getRawFile("HEAD", getReadmeFileName());
            return new String(bytes, FileUtil.detectCharset(bytes));
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * @return the readme file name or {@code null} if the file does not exist
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws GitAPIException the git api exception
     * @throws SVNException the sVN exception
     * @throws ServletException the servlet exception
     */
    public String getReadmeFileName() throws IOException, SVNException, ServletException {
        String baseFileName = "README.md";

        PlayRepository repo = RepositoryService.getRepository(this);

        if (repo.isFile(baseFileName)) {
            return baseFileName;
        }

        if (repo.isFile(baseFileName.toLowerCase())) {
            return baseFileName.toLowerCase();
        }

        if(repo instanceof SVNRepository) {
            baseFileName = "/trunk/" + baseFileName;
            if(repo.isFile(baseFileName)) {
                return baseFileName;
            }

            baseFileName = baseFileName.toLowerCase();
            if(repo.isFile(baseFileName)) {
                return baseFileName;
            }
        }

        return null;
    }

    private boolean isLastIssueNumberCorrect() {
        return issues == null || lastIssueNumber >= issues.size();
    }

    private Long getLastIssueNumber() {
        if (isLastIssueNumberCorrect()) {
            return lastIssueNumber;
        }

        Issue issue = Issue.finder.where().eq("project.id", id).order().desc("number").findList().get(0);
        issue.refresh();

        return issue.number == null ? 0l : issue.number;
    }

    private void setLastIssueNumber(Long number) {
        lastIssueNumber = number;
    }

    private boolean isLastPostingNumberCorrect() {
        return posts == null || lastPostingNumber >= posts.size();
    }

    private Long getLastPostingNumber() {
        if (isLastPostingNumberCorrect()) {
            return lastPostingNumber;
        }

        Posting posting = Posting.finder.where().eq("project.id", id).order().desc("number").findList().get(0);
        posting.refresh();

        return posting.number == null ? 0l : posting.number;
    }

    private void setLastPostingNumber(Long number) {
        lastPostingNumber = number;
    }

    /**
     * 마지막 이슈번호를 증가시킨다.
     *
     * 이슈 추가시 사용한다.
     *
     * @return {@code lastIssueNumber}
     */
    public static Long increaseLastIssueNumber(Long projectId) {
        Project project = find.byId(projectId);
        project.setLastIssueNumber(project.getLastIssueNumber() + 1);
        project.update();

        return project.lastIssueNumber;
    }

    public static void fixLastIssueNumber(Long projectId) {
        Project project = find.byId(projectId);
        project.refresh();
        project.setLastIssueNumber(project.getLastIssueNumber());
        project.update();
    }

    public static Long increaseLastPostingNumber(Long projectId) {
        Project project = find.byId(projectId);
        project.setLastPostingNumber(project.getLastPostingNumber() + 1);
        project.update();

        return project.lastPostingNumber;
    }

    public static void fixLastPostingNumber(Long projectId) {
        Project project = find.byId(projectId);
        project.refresh();
        project.setLastPostingNumber(project.getLastPostingNumber());
        project.update();
    }

    public Resource labelsAsResource() {
        return new Resource() {

            @Override
            public String getId() {
                return id.toString();
            }

            @Override
            public Project getProject() {
                return Project.this;
            }

            @Override
            public ResourceType getType() {
                return ResourceType.PROJECT_LABELS;
            }

        };
    }

    @Override
    public Resource asResource() {
        return new GlobalResource() {

            @Override
            public String getId() {
                return id.toString();
            }

            @Override
            public ResourceType getType() {
                return ResourceType.PROJECT;
            }

        };
    }

    public User getOwnerByLoginId(String loginId){
        return User.findByLoginId(loginId);
    }

    public Boolean attachLabel(Label label) {
        if (labels.contains(label)) {
            // Return false if the label has been already attached.
            return false;
        }

        // Attach new label.
        labels.add(label);
        update();

        return true;
    }

    public void detachLabel(Label label) {
        label.projects.remove(this);
        if (label.projects.size() == 0) {
            label.delete();
        } else {
            label.update();
        }
    }

    public boolean isOwner(User user) {
        return owner.toLowerCase().equals(user.loginId.toLowerCase());
    }

    public String toString() {
        return owner + "/" + name;
    }

    public List<ProjectUser> members() {
        return ProjectUser.findMemberListByProject(this.id);
    }

    /**
     * Return assignable users to this project and group of the project.
     *
     * If the project has no groups, it returns all project members.
     * If the project has a group and is private, it returns all project members and group admins.
     * If the project has a group and is protected or public, it returns all project and group members.
     */
    public List<User> getAssignableUsers() {
        Set<User> users = new HashSet<>();

        // member of this project.
        List<ProjectUser> pus = members();
        for(ProjectUser pu : pus) {
            users.add(pu.user);
        }

        // member of the group
        if(hasGroup()) {
            List<OrganizationUser> ous = (isPublic() || isProtected()) ? this.organization.users : this.organization.getAdmins();
            for(OrganizationUser ou : ous) {
                users.add(ou.user);
            }
        }

        // sorting
        List<User> result = new ArrayList<>(users);
        Collections.sort(result, User.USER_NAME_COMPARATOR);

        if (UserApp.currentUser().isSiteManager()) {
            result.add(UserApp.currentUser());
        }

        return result;
    }

    public boolean isForkedFromOrigin() {
        return this.originalProject != null;
    }

    public boolean hasForks() {
        return this.forkingProjects.size() > 0;
    }

    public List<Project> getForkingProjects() {
        if(this.forkingProjects == null) {
            this.forkingProjects = new ArrayList<>();
        }
        return forkingProjects;
    }

    public void addFork(Project forkProject) {
        getForkingProjects().add(forkProject);
        forkProject.originalProject = this;
    }

    public static List<Project> findByOwnerAndOriginalProject(String loginId, Project originalProject) {
        return find.where()
                .eq("originalProject", originalProject)
                .eq("owner", loginId)
                .findList();
    }

    public void deleteFork() {
        if(this.originalProject != null) {
            this.originalProject.deleteFork(this);
        }
    }

    private void deleteFork(Project project) {
        getForkingProjects().remove(project);
        project.originalProject = null;
    }

    public void fixInvalidForkData() {
        if(originalProject != null) {
            try {
                String owner = originalProject.owner;
            } catch (EntityNotFoundException e) {
                originalProject = null;
                super.update();
            }
        }
    }

    /**
     * @see controllers.ProjectApp#members(String, String)
     */
    @Transactional
    public void cleanEnrolledUsers() {
        List<User> enrolledUsers = this.enrolledUsers;
        List<User> acceptedUsers = new ArrayList<>();
        List<ProjectUser> members = this.members();
        for(ProjectUser projectUser : members) {
            User user = projectUser.user;
            if(enrolledUsers.contains(user)) {
                acceptedUsers.add(user);
            }
        }
        for(User user : acceptedUsers) {
            user.cancelEnroll(this);
            NotificationEvent.afterMemberRequest(this, user, RequestState.ACCEPT);
        }
    }

    public void changeVCS() throws Exception {
        if(this.forkingProjects != null) {
            for(Project fork : forkingProjects) {
                fork.originalProject = null;
                fork.update();
            }
        }

        RepositoryService.deleteRepository(this);
        this.vcs = nextVCS();
        RepositoryService.getRepository(this).create();
        this.update();
    }

    public enum State {
        PUBLIC, PRIVATE, ALL
    }

    /**
     * <pre>Parameter "#1" is not set; SQL statement: delete from issue_comment where (issue_id in (?) [90012-168]]</pre>
     *
     * @see <a href="http://www.avaje.org/bugdetail-420.html">
     *     BUG 420 : SQLException with CascadeType.REMOVE</a>
     */
    @Override
    public void delete() {
        deleteProjectVisitations();
        deleteProjectTransfer();
        deleteFork();
        deleteCommentThreads();
        deletePullRequests();

        if(this.hasForks()) {
            for(Project fork : forkingProjects) {
                fork.deletePullRequests();
                fork.deleteOriginal();
                fork.update();
            }
        }

        for (Label label : labels) {
            label.delete(this);
            label.update();
        }

        for(IssueLabelCategory category : IssueLabelCategory.findByProject(this)) {
            category.delete();
        }

        for(Issue issue : issues) {
            issue.delete();
        }

        for (Assignee assignee : assignees) {
            assignee.delete();
        }

        for(Posting posting : posts) {
            posting.delete();
        }

        super.delete();
    }

    private void deleteProjectVisitations() {
        List<ProjectVisitation> pvs = ProjectVisitation.findByProject(this);
        for (ProjectVisitation pv : pvs) {
            pv.delete();
        }
    }

    private void deleteProjectTransfer() {
        List<ProjectTransfer> pts = ProjectTransfer.findByProject(this);
        for(ProjectTransfer pt : pts) {
            pt.delete();
        }
    }

    private void deleteOriginal() {
        this.originalProject = null;
    }

    private void deletePullRequests() {
        List<PullRequest> sentPullRequests = PullRequest.findSentPullRequests(this);
        for(PullRequest pullRequest : sentPullRequests) {
            CommentThread.deleteByPullRequest(pullRequest);
            pullRequest.delete();
        }

        List<PullRequest> allReceivedRequests = PullRequest.allReceivedRequests(this);
        for(PullRequest pullRequest : allReceivedRequests) {
            CommentThread.deleteByPullRequest(pullRequest);
            pullRequest.delete();
        }
    }

    private void deleteCommentThreads() {
        for(CommentThread commentThread : this.commentThreads) {
            commentThread.delete();
        }
    }

    public static String newProjectName(String loginId, String projectName) {
        Project project = Project.findByOwnerAndProjectName(loginId, projectName);
        if(project == null) {
            return projectName;
        }

        for(int i = 1 ; ; i++) {
            String newProjectName = projectName + "-" + i;
            project = Project.findByOwnerAndProjectName(loginId, newProjectName);
            if(project == null) {
                return newProjectName;
            }
        }
    }

    /**
     * @see #newProjectName(String, String)
     */
    public static Project copy(Project project, String owner) {
        Project copyProject = new Project();
        copyProject.name = newProjectName(owner, project.name);
        copyProject.overview = project.overview;
        copyProject.vcs = project.vcs;
        copyProject.owner = owner;
        copyProject.projectScope = project.projectScope;
        copyProject.menuSetting = new ProjectMenuSetting(project.menuSetting);
        copyProject.menuSetting.project = copyProject;
        copyProject.menuSetting.save();

        return copyProject;
    }

    @Override
    public Set<Label> getLabels() {
        return labels;
    }

    public static int countProjectsJustMemberAndNotOwner(String loginId) {
        return find.where().eq("projectUser.user.loginId", loginId)
                .ne("owner", loginId).findRowCount();
    }

    public static int countProjectsCreatedByUser(String loginId) {
        return find.where().eq("owner", loginId).findRowCount();
    }

    public List<PushedBranch> getRecentlyPushedBranches() {
        return PushedBranch.find.where()
                            .eq("project", this)
                            .gt("pushedDate", DateTime.now().minusMillis(DRAFT_TIME_IN_MILLIS).toDate())
                            .findList();
    }

    public List<PushedBranch> getOldPushedBranches() {
        return PushedBranch.find.where()
                            .eq("project", this)
                            .lt("pushedDate", DateTime.now().minusMillis(DRAFT_TIME_IN_MILLIS).toDate())
                            .findList();
    }

    public boolean isGit() {
        return vcs.equals("GIT");
    }

    public List<Project> getAssociationProjects() {
        List<Project> projects = new ArrayList<>();
        projects.add(this);
        projects.addAll(forkingProjects);
        if(isForkedFromOrigin() && originalProject.menuSetting.code
                && originalProject.menuSetting.pullRequest) {
                projects.add(originalProject);
        }
        return projects;
    }

    public int getMaxNumberOfRequiredReviewerCount() {
        List<ProjectUser> members = ProjectUser.findMemberListByProject(this.id);
        if(members.size() > 1) {
            return members.size();
        } else {
            return 1;
        }
    }

    public int getWatchingCount() {
        Resource resource = this.asResource();
        return Watch.countBy(resource.getType(), resource.getId());
    }

    public boolean hasGroup() {
        return this.organization != null;
    }

    public boolean isPublic() {
        return projectScope == ProjectScope.PUBLIC;
    }

    public boolean isProtected() {
        return projectScope == ProjectScope.PROTECTED;
    }

    public boolean isPrivate() {
        return projectScope == ProjectScope.PRIVATE;
    }

    public String nextVCS() {
        if(this.vcs.equals(RepositoryService.VCS_GIT)) {
            return RepositoryService.VCS_SUBVERSION;
        } else {
            return RepositoryService.VCS_GIT;
        }
    }
}
TOP

Related Classes of models.Project

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.