diff --git a/server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java b/server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java index 7b399147ac..4fba38b170 100644 --- a/server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java +++ b/server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java @@ -1175,4 +1175,45 @@ private void migrate12(VersionedYamlDoc doc, Stack versions) { } } + @SuppressWarnings("unused") + private void migrate13(VersionedYamlDoc doc, Stack versions) { + for (NodeTuple specTuple: doc.getValue()) { + String specTupleKey = ((ScalarNode)specTuple.getKeyNode()).getValue(); + if (specTupleKey.equals("jobs")) { + SequenceNode jobsNode = (SequenceNode) specTuple.getValueNode(); + for (Node jobsNodeItem: jobsNode.getValue()) { + MappingNode jobNode = (MappingNode) jobsNodeItem; + for (Iterator itJobTuple = jobNode.getValue().iterator(); itJobTuple.hasNext();) { + NodeTuple jobTuple = itJobTuple.next(); + String jobTupleKey = ((ScalarNode)jobTuple.getKeyNode()).getValue(); + if (jobTupleKey.equals("projectDependencies")) { + SequenceNode projectDependenciesNode = (SequenceNode) jobTuple.getValueNode(); + for (Node projectDependenciesItem: projectDependenciesNode.getValue()) { + MappingNode projectDependencyNode = (MappingNode) projectDependenciesItem; + for (Iterator itProjectDependencyTuple = projectDependencyNode.getValue().iterator(); + itProjectDependencyTuple.hasNext();) { + NodeTuple projectDependencyTuple = itProjectDependencyTuple.next(); + ScalarNode projectDependencyTupleKeyNode = ((ScalarNode)projectDependencyTuple.getKeyNode()); + if (projectDependencyTupleKeyNode.getValue().equals("projectName")) + projectDependencyTupleKeyNode.setValue("projectPath"); + } + } + } + } + } + } else if (specTupleKey.equals("imports")) { + SequenceNode importsNode = (SequenceNode) specTuple.getValueNode(); + for (Node importsNodeItem: importsNode.getValue()) { + MappingNode importNode = (MappingNode) importsNodeItem; + for (Iterator itImportTuple = importNode.getValue().iterator(); itImportTuple.hasNext();) { + NodeTuple importTuple = itImportTuple.next(); + ScalarNode importTupleKeyNode = (ScalarNode)importTuple.getKeyNode(); + if (importTupleKeyNode.getValue().equals("projectName")) + importTupleKeyNode.setValue("projectPath"); + } + } + } + } + } + } diff --git a/server-core/src/main/java/io/onedev/server/buildspec/job/Job.java b/server-core/src/main/java/io/onedev/server/buildspec/job/Job.java index 0400712912..b2e845c678 100644 --- a/server-core/src/main/java/io/onedev/server/buildspec/job/Job.java +++ b/server-core/src/main/java/io/onedev/server/buildspec/job/Job.java @@ -369,9 +369,9 @@ public boolean isValid(ConstraintValidatorContext context) { } } - Set dependencyProjectNames = new HashSet<>(); + Set dependencyProjectPaths = new HashSet<>(); for (ProjectDependency dependency: projectDependencies) { - if (!dependencyProjectNames.add(dependency.getProjectPath())) { + if (!dependencyProjectPaths.add(dependency.getProjectPath())) { isValid = false; context.buildConstraintViolationWithTemplate("Duplicate dependency (" + dependency.getProjectPath() + ")") .addPropertyNode("projectDependencies").addConstraintViolation(); diff --git a/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultProjectManager.java b/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultProjectManager.java index 87f175049f..48032c3d56 100644 --- a/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultProjectManager.java +++ b/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultProjectManager.java @@ -346,8 +346,11 @@ public void delete(Project project) { public Project find(String path) { List names = Splitter.on("/").omitEmptyStrings().trimResults().splitToList(path); Project project = null; - for (String name: names) + for (String name: names) { project = find(project, name); + if (project == null) + break; + } return project; } diff --git a/server-core/src/main/java/io/onedev/server/migration/DataMigrator.java b/server-core/src/main/java/io/onedev/server/migration/DataMigrator.java index 8bc380cba7..75dc9aaa50 100644 --- a/server-core/src/main/java/io/onedev/server/migration/DataMigrator.java +++ b/server-core/src/main/java/io/onedev/server/migration/DataMigrator.java @@ -17,7 +17,10 @@ import java.util.Set; import java.util.Stack; import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; import javax.inject.Singleton; import org.apache.commons.io.IOUtils; @@ -3113,4 +3116,100 @@ private void migrate67(File dataDir, Stack versions) { } } + private void migrateAttachmentLinks(@Nullable Element element, Map projectIds) { + if (element != null) { + String content = element.getText(); + Pattern pattern = Pattern.compile("/projects/([\\w-\\.]+)/attachment/"); + Matcher matcher = pattern.matcher(content); + StringBuffer buffer = new StringBuffer(); + + while (matcher.find()) { + String projectName = matcher.group(1); + String projectId = projectIds.get(projectName); + if (projectId != null) + matcher.appendReplacement(buffer, "/projects/" + projectId + "/attachment/"); + else + matcher.appendReplacement(buffer, matcher.group()); + } + + matcher.appendTail(buffer); + element.setText(buffer.toString()); + } + } + + private void migrate68(File dataDir, Stack versions) { + Map projectIds = new HashMap<>(); + for (File file: dataDir.listFiles()) { + if (file.getName().startsWith("Projects.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + projectIds.put(element.elementTextTrim("name"), element.elementTextTrim("id")); + } + } + + for (File file: dataDir.listFiles()) { + if (file.getName().startsWith("Projects.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + element.addElement("codeManagementEnabled").setText("true"); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("Issues.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + migrateAttachmentLinks(element.element("description"), projectIds); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("IssueComments.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + migrateAttachmentLinks(element.element("content"), projectIds); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("PullRequests.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + migrateAttachmentLinks(element.element("description"), projectIds); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("PullRequestComments.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + migrateAttachmentLinks(element.element("content"), projectIds); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("CodeComments.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + migrateAttachmentLinks(element.element("content"), projectIds); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("CodeCommentReplys.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + migrateAttachmentLinks(element.element("content"), projectIds); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("Groups.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + element.addElement("createRootProjects").setText("false"); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("Roles.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) + element.addElement("createChildren").setText("false"); + dom.writeToFile(file, false); + } else if (file.getName().startsWith("Settings.xml")) { + VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file); + for (Element element: dom.getRootElement().elements()) { + if (element.elementTextTrim("key").equals("JOB_EXECUTORS")) { + Element valueElement = element.element("value"); + for (Element executorElement: valueElement.elements()) { + Element jobMatchElement = executorElement.element("jobMatch"); + if (jobMatchElement.getTextTrim().equals("all")) + jobMatchElement.detach(); + else + jobMatchElement.setName("jobRequirement"); + } + } + } + dom.writeToFile(file, false); + } + } + } + } diff --git a/server-core/src/main/java/io/onedev/server/web/page/admin/group/authorization/GroupAuthorizationsPage.html b/server-core/src/main/java/io/onedev/server/web/page/admin/group/authorization/GroupAuthorizationsPage.html index d17695c3fd..eab808477f 100644 --- a/server-core/src/main/java/io/onedev/server/web/page/admin/group/authorization/GroupAuthorizationsPage.html +++ b/server-core/src/main/java/io/onedev/server/web/page/admin/group/authorization/GroupAuthorizationsPage.html @@ -1,4 +1,8 @@ +
+ + When authorize a project, all child projects will also be authorized with the role +
diff --git a/server-core/src/main/java/io/onedev/server/web/page/admin/user/authorization/UserAuthorizationsPage.html b/server-core/src/main/java/io/onedev/server/web/page/admin/user/authorization/UserAuthorizationsPage.html index 1e54bce4f8..f875ebc1fc 100644 --- a/server-core/src/main/java/io/onedev/server/web/page/admin/user/authorization/UserAuthorizationsPage.html +++ b/server-core/src/main/java/io/onedev/server/web/page/admin/user/authorization/UserAuthorizationsPage.html @@ -1,4 +1,8 @@ +
+ + When authorize a project, all child projects will also be authorized with the role +
diff --git a/server-core/src/main/java/io/onedev/server/web/page/project/blob/project-blob.css b/server-core/src/main/java/io/onedev/server/web/page/project/blob/project-blob.css index 8c03e55b6f..cdee86032f 100644 --- a/server-core/src/main/java/io/onedev/server/web/page/project/blob/project-blob.css +++ b/server-core/src/main/java/io/onedev/server/web/page/project/blob/project-blob.css @@ -37,7 +37,7 @@ } .floating.get-code { - max-width: 480px !important; + width: 480px !important; } .floating.get-code .download { padding: 0 2.25rem 2rem 2.25rem; diff --git a/server-core/src/main/java/io/onedev/server/web/page/project/setting/authorization/ProjectAuthorizationsPage.html b/server-core/src/main/java/io/onedev/server/web/page/project/setting/authorization/ProjectAuthorizationsPage.html index ba6ea1a47c..43f6d7b571 100644 --- a/server-core/src/main/java/io/onedev/server/web/page/project/setting/authorization/ProjectAuthorizationsPage.html +++ b/server-core/src/main/java/io/onedev/server/web/page/project/setting/authorization/ProjectAuthorizationsPage.html @@ -1,4 +1,8 @@ +
+ + When authorize a user, the user will also be authorized with the role for all child projects +
diff --git a/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/DefaultRoleBean.java b/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/DefaultRoleBean.java index fc77bbea84..ae06fcdbcb 100644 --- a/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/DefaultRoleBean.java +++ b/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/DefaultRoleBean.java @@ -8,6 +8,7 @@ import io.onedev.server.entitymanager.RoleManager; import io.onedev.server.model.Role; import io.onedev.server.web.editable.annotation.Editable; +import io.onedev.server.web.editable.annotation.NameOfEmptyValue; import io.onedev.server.web.editable.annotation.RoleChoice; @Editable @@ -17,8 +18,10 @@ public class DefaultRoleBean implements Serializable { private String roleName; - @Editable(name="Default Role", description="Default role determines default permissions granted to everyone in the system") + @Editable(name="Default Role", description="Default role determines default permissions granted to everyone in the system. " + + "All child projects will also have this default role") @RoleChoice + @NameOfEmptyValue("No default role") public String getRoleName() { return roleName; } diff --git a/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/ParentBean.java b/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/ParentBean.java index 12d0dd75fa..a07e5dbe34 100644 --- a/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/ParentBean.java +++ b/server-core/src/main/java/io/onedev/server/web/page/project/setting/general/ParentBean.java @@ -8,6 +8,7 @@ import io.onedev.server.entitymanager.ProjectManager; import io.onedev.server.model.Project; import io.onedev.server.web.editable.annotation.Editable; +import io.onedev.server.web.editable.annotation.NameOfEmptyValue; import io.onedev.server.web.editable.annotation.ParentChoice; @Editable @@ -17,8 +18,10 @@ public class ParentBean implements Serializable { private String parentPath; - @Editable(name="Parent Project") + @Editable(name="Parent Project", description="Settings and permissions of parent project will be inherited " + + "by this project") @ParentChoice + @NameOfEmptyValue("No parent") public String getParentPath() { return parentPath; }