This is an automated email from the git hooks/post-receive script. New commit to branch feature/GIT in repository scmwebeditor. See http://git.nuiton.org/scmwebeditor.git commit ced7ac560216e6fcb3ebe26d29f4ee786b0e7f30 Author: InternationalKoder <international_koder@yahoo.com> Date: Thu Apr 23 11:26:24 2015 +0200 First Git version --- pom.xml | 12 + .../org/nuiton/scmwebeditor/GitConnection.java | 457 +++++++++++++++++ .../org/nuiton/scmwebeditor/ScmConnection.java | 88 ++++ .../nuiton/scmwebeditor/ScmConnectionFactory.java | 48 ++ .../scmwebeditor/ScmConnectionInterface.java | 84 ++++ .../scmwebeditor/ScmWebEditorBaseAction.java | 172 +------ .../nuiton/scmwebeditor/ScmWebEditorConfig.java | 7 + .../scmwebeditor/ScmWebEditorConfigOption.java | 3 +- .../org/nuiton/scmwebeditor/SvnConnection.java | 540 ++++++++++++++++++--- .../nuiton/scmwebeditor/SweSessionListener.java | 42 ++ .../nuiton/scmwebeditor/actions/ResetAction.java | 4 + .../nuiton/scmwebeditor/actions/SaveAction.java | 4 +- .../actions/ScmWebEditorCommitAction.java | 211 +------- .../actions/ScmWebEditorMainAction.java | 52 +- .../nuiton/scmwebeditor/actions/SearchAction.java | 254 ++++------ .../nuiton/scmwebeditor/actions/UploadAction.java | 14 +- .../resources/i18n/scmwebeditor_en_GB.properties | 3 + .../resources/i18n/scmwebeditor_fr_FR.properties | 3 + src/main/resources/log4j.properties | 39 +- src/main/resources/scmwebeditor.properties | 1 + src/main/resources/struts.xml | 9 +- .../webapp/WEB-INF/content/modificationViewer.jsp | 54 +-- src/main/webapp/WEB-INF/content/outConnection.jsp | 16 +- src/main/webapp/WEB-INF/content/search.jsp | 19 +- src/main/webapp/WEB-INF/content/uploadForm.jsp | 2 +- src/main/webapp/css/main.css | 18 +- src/main/webapp/js/branches.js | 36 ++ src/main/webapp/js/selectLanguage.js | 2 +- .../org/nuiton/scmwebeditor/BaseActionTest.java | 2 +- 29 files changed, 1517 insertions(+), 679 deletions(-) diff --git a/pom.xml b/pom.xml index 995f3cb..962619d 100644 --- a/pom.xml +++ b/pom.xml @@ -241,6 +241,18 @@ <version>${nuitonWebVersion}</version> </dependency> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>3.7.0.201502260915-r</version> + </dependency> + + <dependency> + <groupId>org.webjars</groupId> + <artifactId>codemirror</artifactId> + <version>5.1</version> + </dependency> + <!-- Struts --> <dependency> diff --git a/src/main/java/org/nuiton/scmwebeditor/GitConnection.java b/src/main/java/org/nuiton/scmwebeditor/GitConnection.java new file mode 100644 index 0000000..6299fac --- /dev/null +++ b/src/main/java/org/nuiton/scmwebeditor/GitConnection.java @@ -0,0 +1,457 @@ +/* + * #%L + * ScmWebEditor + * %% + * Copyright (C) 2009 - 2014 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.scmwebeditor; + +import com.google.common.collect.Lists; +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.jgit.api.*; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.nuiton.scmwebeditor.actions.ScmWebEditorCommitAction; +import org.nuiton.scmwebeditor.actions.SearchAction; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.*; + +public class GitConnection extends ScmConnection { + + private static final Log log = LogFactory.getLog(GitConnection.class); + + /** git existing repository */ + protected Repository gitRepo; + + /** the local directory where the repository has been cloned to */ + protected File localDirectory; + + protected static final String MASTER_BRANCH = "master"; + + public Repository getGitRepo() { return gitRepo; } + + public String getScmPath() { return scmPath; } + + public File getLocalDirectory() { return localDirectory; } + + @Override + public String getFullFileName() { + + String fullFileName = localDirectory + "/" + fileName; + return fullFileName; + } + + + + public GitConnection(String address, String sessionId) throws IOException { + + super(address.substring(0, address.indexOf(".git") + 4)); + + if(log.isDebugEnabled()) { + log.debug("Git repository"); + } + + scmPath = addressScm; + fileName = address.substring(address.indexOf(".git") + 4); + + // Cloning the remote repository to a local directory + String localReposPath = ScmWebEditorConfig.getLocalRepositoriesPath(); + localDirectory = new File(localReposPath + "/" + sessionId); + + // We don't need to clone the repository if we already have the last version + boolean cloneNeeded = false; + + if (!localDirectory.exists()) { + cloneNeeded = true; + } else { + Git git = Git.open(localDirectory); + DiffCommand diff = git.diff(); + try { + + List<DiffEntry> diffs = diff.call(); + + // If there are differences, we delete the current version and clone the last one + if(diffs.size() > 0) { + FileUtils.deleteDirectory(localDirectory); + cloneNeeded = true; + if (log.isDebugEnabled()) { + log.debug("The current version is not the last one, cloning is required"); + } + } else { + + if (log.isDebugEnabled()) { + log.debug("The current version is the last one, cloning is not required"); + } + } + } catch (GitAPIException e) { + if (log.isErrorEnabled()) { + log.error("The repository at address " + addressScm + " doesn't exist", e); + } + } + } + + if (cloneNeeded) { + CloneCommand clone = Git.cloneRepository(); + clone.setURI(addressScm); + clone.setDirectory(localDirectory); + try { + clone.call(); + } catch (GitAPIException e) { + if (log.isErrorEnabled()) { + log.error("Can't clone the remote repository"); + } + } + } + + // Connection to the local repository + File gitFile = new File(localDirectory.getAbsolutePath() + "/.git"); + FileRepositoryBuilder gitRepoBuilder = new FileRepositoryBuilder(); + gitRepoBuilder.setGitDir(gitFile); + gitRepo = gitRepoBuilder.build(); + + if (!gitRepo.getObjectDatabase().exists()) { + + if (log.isErrorEnabled()) { + log.error("The repository at address " + addressScm + " doesn't exist"); + } + } + } + + + @Override + public String search(SearchAction action) { + + String url; + + String headBranchName = null; + try { + headBranchName = gitRepo.getBranch(); + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Error while getting the head branch name", e); + } + } + action.setHeadBranchName(headBranchName); + + action.setFiles(new LinkedList<String>()); + action.setDirectories(new HashMap<String, String>()); + + ObjectId commitId = null; + + // getting the requested branch, or the last commit if none was requested + try { + commitId = gitRepo.resolve(Constants.HEAD); + + String selectedBranch = action.getSelectedBranch(); + + if (selectedBranch != null) { + if (!selectedBranch.equals("")) { + + changeBranch(selectedBranch); + commitId = gitRepo.resolve(selectedBranch); + + // if the given branch was not found, we use the master branch instead + if (commitId == null) { + + if (log.isDebugEnabled()) { + log.debug("Branch " + selectedBranch + " was not found, using " + MASTER_BRANCH + "instead"); + } + + action.setSelectedBranch(MASTER_BRANCH); + changeBranch(MASTER_BRANCH); + commitId = gitRepo.resolve(MASTER_BRANCH); + } + } + } + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can't access to the repository", e); + } + } + + String id = action.getId(); + String address = action.getAddress(); + + // if the id parameter is not given, we look for the root + if (id.equals("")) { + return SearchAction.ROOT; + } else if (id.equals("0")) { + url = address; + } else { + url = id; + } + + // if the id parameter is given, we look for the specified directory + + try { + + if (!gitRepo.getObjectDatabase().exists()) { + + action.setError("The repository at address " + address + " doesn't exist"); + } + + RevWalk revWalk = new RevWalk(gitRepo); + RevCommit commit = revWalk.parseCommit(commitId); + RevTree tree = commit.getTree(); + + // making a list of the repository's files and directories + TreeWalk treeWalk; + + treeWalk = new TreeWalk(gitRepo); + treeWalk.addTree(tree); + treeWalk.setRecursive(false); + + // the directories we have to open to find the requested url + ArrayList<String> dirs = Lists.newArrayList(url.substring(url.indexOf(".git") + 4).split("/")); + + while (treeWalk.next()) { + + String fileName = address + "/" + treeWalk.getPathString(); + + if (fileName.startsWith(url) || url.startsWith(fileName)) { + + // subtree = directory + if (treeWalk.isSubtree()) { + + String pathString = treeWalk.getPathString(); + String currentDir = pathString.substring(pathString.lastIndexOf("/") + 1); + + if (dirs.contains(currentDir)) { + treeWalk.enterSubtree(); + } else { + action.getDirectories().put(fileName, fileName); + } + } else { + action.getFiles().add(fileName); + } + } + } + + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can't access to the repository", e); + } + } + + return SearchAction.SUCCESS; + } + + + /** + * Gives a list of a remote repository's branches + * @param address the URL to the remote repository + * @return a list of the repository's branches + */ + public static List<String> getBranches(String address) { + + List<String> branches = new ArrayList<String>(); + + LsRemoteCommand lsRemote = new LsRemoteCommand(null); + lsRemote.setRemote(address); + lsRemote.setTags(false); + lsRemote.setHeads(true); + + try { + Collection<Ref> lsRemoteResult = lsRemote.call(); + branches = new ArrayList<String>(); + + for (Ref branch : lsRemoteResult) { + + // we only take the name of the branch, not "refs/heads/" + String name = branch.getName(); + name = name.substring(name.indexOf("/") + 1); + name = name.substring(name.indexOf("/") + 1); + + branches.add(name); + } + } catch (GitAPIException e) { + log.error("The repository at address " + address + " doesn't exist", e); + } + + return branches; + } + + + @Override + public String getUUID() { + return null; + } + + + @Override + public String getHeadRevision(String login, String password) throws IOException { + + String dir = localDirectory.getAbsolutePath(); + File fileToEdit = new File(dir + "/" + fileName); + + String origText = FileUtils.readFileToString(fileToEdit); + + return origText; + } + + @Override + public String getHeadNumberRevision(String login, String password) throws IOException, GitAPIException { + + Git git = Git.open(localDirectory); + DescribeCommand describeCommand = git.describe(); + + String headRevision = describeCommand.call(); + + return headRevision; + } + + + /** + * Changing for another branch + * @param branchName the new branch's name + * @throws IOException if reaching the repository is not possible + */ + public void changeBranch(String branchName) throws IOException { + + Git git = Git.open(localDirectory); + + CheckoutCommand checkout = git.checkout(); + checkout.setCreateBranch(true); + checkout.setName(branchName); + checkout.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK); + checkout.setStartPoint("origin/" + branchName); + + try { + checkout.call(); + } catch (GitAPIException e) { + + // if we could not create a new local branch, it may be because it already exists + checkout = git.checkout(); + checkout.setName(branchName); + + try { + checkout.call(); + } catch (GitAPIException e1) { + if (log.isErrorEnabled()) { + log.error("Can not checkout branch " + branchName, e1); + } + } + } + } + + @Override + public String commit(ScmWebEditorCommitAction action, String login, String password, + String address, String commitMessage) { + + if (log.isDebugEnabled()) { + log.debug("Entering Git commit"); + } + + File localFile = new File(getFullFileName()); + + action.setLastText(action.getNewText()); + + try { + String originalText = FileUtils.readFileToString(localFile); + + action.setOrigText(originalText); + + } catch (FileNotFoundException e) { + log.error("Can not find the local file", e); + + return action.ERROR; + } catch (IOException e) { + log.error("Can not open the local file", e); + + return action.ERROR; + } + + // authentication + //UsernamePasswordCredentialsProvider userPassProvider = + + // applying the changes on the local file + try { + FileUtils.writeStringToFile(localFile, action.getNewText()); + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can not modify the local file", e); + } + + return action.ERROR; + } + + try { + // commit + Git git = Git.open(localDirectory); + + if (log.isDebugEnabled()) { + log.debug("Preparing commit"); + } + + CommitCommand commit = git.commit(); + commit.setAll(true); + commit.setAuthor("SCMWebEditor", "mail@scmwebeditor.com"); + commit.setMessage(action.getCommitMessage()); + + try { + commit.call(); + } catch (GitAPIException e) { + if (log.isErrorEnabled()) { + log.error("Can not commit", e); + } + + return action.ERROR; + } + + if (log.isDebugEnabled()) { + log.debug("Preparing push"); + } + + // push + PushCommand push = git.push(); + push.setRemote(addressScm); + + try { + push.call(); + } catch (GitAPIException e) { + if (log.isErrorEnabled()) { + log.error("Can not push", e); + } + + return action.ERROR; + } + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can not open git local repository : " + localDirectory.getAbsolutePath(), e); + + return action.ERROR; + } + } + + return action.SUCCESS; + } +} diff --git a/src/main/java/org/nuiton/scmwebeditor/ScmConnection.java b/src/main/java/org/nuiton/scmwebeditor/ScmConnection.java new file mode 100644 index 0000000..c454f05 --- /dev/null +++ b/src/main/java/org/nuiton/scmwebeditor/ScmConnection.java @@ -0,0 +1,88 @@ +/* + * #%L + * ScmWebEditor + * %% + * Copyright (C) 2009 - 2014 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.scmwebeditor; + +import org.nuiton.scmwebeditor.actions.ScmWebEditorCommitAction; +import org.nuiton.scmwebeditor.actions.SearchAction; + +public abstract class ScmConnection { + + /** full scm path */ + protected String addressScm; + + /** name of the file to edit */ + protected String fileName; + + /** path to the file to edit's directory */ + protected String scmPath; + + public String getFileName() { return fileName; } + + /** the full name which can be used to open the file */ + public abstract String getFullFileName(); + + + public ScmConnection(String address) { + + addressScm = address; + } + + + /** + * Makes a list of the repository's files + * @param action the action which was called for the search + * @return a code which will be interpreted in struts.xml + */ + public abstract String search(SearchAction action); + + /** + * @return the repository's UUID or null if the repository doesn't have one + */ + public abstract String getUUID(); + + public abstract String getScmPath(); + + /** + * Gives the edited file's content for its head revision + * @param login the username to login with + * @param password the password to login with + * @return the edited file's content in a String + * @throws Exception when an error occurs while getting the content + */ + public abstract String getHeadRevision(String login, String password) throws Exception; + + /** + * @param login the username to login with + * @param password the password to login with + * @return the number of the head revision + */ + public abstract String getHeadNumberRevision(String login, String password) throws Exception; + + /** + * commits the changes + * @param action the action which was called for the commit + * @return the return code + */ + public abstract String commit(ScmWebEditorCommitAction action, String login, String password, + String address, String commitMessage); + +} diff --git a/src/main/java/org/nuiton/scmwebeditor/ScmConnectionFactory.java b/src/main/java/org/nuiton/scmwebeditor/ScmConnectionFactory.java new file mode 100644 index 0000000..d1c5937 --- /dev/null +++ b/src/main/java/org/nuiton/scmwebeditor/ScmConnectionFactory.java @@ -0,0 +1,48 @@ +package org.nuiton.scmwebeditor; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +/** + * Allows to create a ScmConnection + */ +public class ScmConnectionFactory { + + private static final Log log = LogFactory.getLog(ScmConnectionFactory.class); + + + /** + * Creates a new ScmConnection, the type of SCM is deduced with the repository's address + * @param address the SCM's address + * @param sessionId the user's session ID + * @return a ScmConnection of the correct type to use the repository at the given address + */ + public static ScmConnection createScmConnection(String address, String sessionId) { + + ScmConnection scmConn = null; + + if(address.endsWith(".git")) { + + try { + scmConn = new GitConnection(address, sessionId); + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can not reach the repository", e); + } + } + } else { + + try { + scmConn = new SvnConnection(address); + } catch(StringIndexOutOfBoundsException e) { + if (log.isDebugEnabled()) { + log.debug("Parameter is not valid ", e); + } + } + } + + return scmConn; + } +} diff --git a/src/main/java/org/nuiton/scmwebeditor/ScmConnectionInterface.java b/src/main/java/org/nuiton/scmwebeditor/ScmConnectionInterface.java new file mode 100644 index 0000000..50bf2c2 --- /dev/null +++ b/src/main/java/org/nuiton/scmwebeditor/ScmConnectionInterface.java @@ -0,0 +1,84 @@ +package org.nuiton.scmwebeditor; + +import org.nuiton.scmwebeditor.actions.*; + +/** + * An interface which the SCM classes have to implement + */ +public interface ScmConnectionInterface { + + /** + * Searches the repository's files to make a list of them + * @param action the SearchAction which contains the parameters (repository's address, username, password...) + * and which will contain the result (a list of TreeNode for example) + * @return SearchAction.AUTH_ERROR if there has been a problem during the authentication + * SearchAction.ROOT if the result is the root of the repository + * SearchAction.SUCCESS otherwise + */ + public String search(SearchAction action); + + + /** + * Makes a commit of the changed made to the edited file + * @param action the ScmWebEditorCommitAction which contains the parameters (repository's address, username, + * password, new file content...) and which will contain the result (new revision number, + * information message...) + * @return ScmWebEditorCommitAction.ERROR if an error occurred + * ScmWebEditorCommitAction.LOGIN if the authentication failed + * ScmWebEditorCommitAction.ERROR_PATH if it is impossible to reach the repository + * ScmWebEditorCommitAction.FILE_MODIFY if the local file has been modified by another program + * ScmWebEditorCommitAction.SUCCESS if the commit has been done without any problem + */ + public String commit(ScmWebEditorCommitAction action); + + + /** + * Cancels all the changes made on the edited file by getting its last revision + * @param action the ResetAction which contains the parameters (repository's address, username, password...) + * and which will contain the result (the last revision's content and the revision number) + * @return ResetAction.AUTH_ERROR if the authentication failed + * ResetAction.ERROR_PATH if it is impossible to reach the repository + * ResetAction.SUCCESS if the reset has been done without any problem + */ + public String reset(ResetAction action); + + + /** + * Logs the user out of the current SCM + * @param action the LogoutAction which contains the parameters (repository's address, servlet request, SCM session...) + * @return LogoutAction.SUCCESS if the logout has been done without any problem + */ + public String logout(LogoutAction action); + + + /** + * Uploads a file to the repository as a new file + * @param action the UploadAction which contains the parameters (repository's address, file to upload...) + * @return UploadAction.ERROR if an error occurred + * UploadAction.REDIRECT if there is no file to upload + * UploadAction.LOGIN if the authentication failed + * UploadAction.SUCCESS if the upload has been done without any problem + */ + public String uploadFile(UploadAction action); + + + /** + * Gives the content of a file as a String + * @param path the path to the file to get the content from + * @param username the user's login for the SCM + * @param password the user's password for the SCM + * @return a String which contains the file's content + */ + public String getFileContent(String path, String username, String password); + + + /** + * Gives the number of the head revision + * @param path the path to the SCM + * @param username the user's login for the SCM + * @param password the user's password for the SCM + * @return a String which contains the head revision's number + */ + public String getHeadRevisionNumber(String path, String username, String password); + +} diff --git a/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorBaseAction.java b/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorBaseAction.java index c78bdc5..970942b 100644 --- a/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorBaseAction.java +++ b/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorBaseAction.java @@ -22,50 +22,30 @@ package org.nuiton.scmwebeditor; import com.opensymphony.xwork2.ActionContext; -import info.monitorenter.cpdetector.io.ASCIIDetector; -import info.monitorenter.cpdetector.io.ByteOrderMarkDetector; -import info.monitorenter.cpdetector.io.CodepageDetectorProxy; -import info.monitorenter.cpdetector.io.JChardetFacade; -import info.monitorenter.cpdetector.io.ParsingDetector; -import org.apache.commons.io.FileUtils; +import info.monitorenter.cpdetector.io.*; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.struts2.interceptor.ServletRequestAware; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; import org.apache.tika.parser.AutoDetectParser; import org.apache.tika.sax.BodyContentHandler; import org.nuiton.web.struts2.BaseAction; -import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNNodeKind; -import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; -import org.tmatesoft.svn.core.io.SVNRepository; -import org.tmatesoft.svn.core.io.SVNRepositoryFactory; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNUpdateClient; import org.tmatesoft.svn.core.wc.SVNWCClient; import org.tmatesoft.svn.core.wc.SVNWCUtil; import org.xml.sax.SAXException; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; +import java.io.*; import java.net.MalformedURLException; import java.nio.charset.Charset; import java.util.Map; @@ -76,7 +56,7 @@ import java.util.Properties; * Date: 24 nov. 2009 * Time: 21:24:39 */ -public class ScmWebEditorBaseAction extends BaseAction { +public class ScmWebEditorBaseAction extends BaseAction implements ServletRequestAware { public static final String AUTH_ERROR = "authError"; @@ -84,6 +64,8 @@ public class ScmWebEditorBaseAction extends BaseAction { protected Map<String, Object> session; + protected transient HttpServletRequest request; + private static final long serialVersionUID = 1L; final static protected String CONTEXT_ACTION_KEY = "action"; @@ -241,51 +223,6 @@ public class ScmWebEditorBaseAction extends BaseAction { } } - /** - * @param svnSess - * @param checkoutdir - * @param numVersion - * @throws SVNException - */ - public void checkout(SvnConnection svnSess, File checkoutdir, String numVersion) throws SVNException { - - SVNUpdateClient upclient = new SVNUpdateClient(svnSess.getManager(), svnSess.getSvnOption()); - - if (log.isDebugEnabled()) { - log.debug("Do Checkout of " + svnSess.getRemoteUrl()); - } - try { - upclient.doCheckout(svnSess.getRemoteUrl(), checkoutdir, - SVNRevision.create(Long.parseLong(numVersion)), SVNRevision.create(Long.parseLong(numVersion)), SVNDepth.FILES, false); - } catch (NumberFormatException e) { - if (log.isErrorEnabled()) { - log.error("The number version is not valid."); - } - upclient.doCheckout(svnSess.getRemoteUrl(), checkoutdir, - SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.FILES, false); - - } - - - } - - /** - * @param svnSess - * @param checkoutdir - * @throws SVNException - */ - public void checkout(SvnConnection svnSess, File checkoutdir) throws SVNException { - - SVNUpdateClient upclient = new SVNUpdateClient(svnSess.getManager(), svnSess.getSvnOption()); - - if (log.isDebugEnabled()) { - log.debug("Do Checkout of " + svnSess.getRemoteUrl()); - } - - upclient.doCheckout(svnSess.getRemoteUrl(), checkoutdir, - SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.FILES, false); - } - /** * @param address @@ -295,73 +232,25 @@ public class ScmWebEditorBaseAction extends BaseAction { * @throws SVNException * @throws IllegalArgumentException */ - public String getHeadRevision(String address, String login, String password) throws SVNException, IllegalArgumentException { - - String lastRevision; - - SvnConnection svnConn = new SvnConnection(address); + public String getHeadRevision(String address, String login, String password) throws Exception, IllegalArgumentException { + HttpSession session = request.getSession(); + String sessionId = session.getId(); + ScmConnection scmConn = ScmConnectionFactory.createScmConnection(address, sessionId); - String url = svnConn.getSvnPath(); - String file = svnConn.getFileName(); - - - SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url)); - ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(login, password); - repository.setAuthenticationManager(authManager); - - SVNNodeKind nodeKind = repository.checkPath(file, -1); - - - if (nodeKind == SVNNodeKind.NONE) { - if (log.isErrorEnabled()) { - log.error("There is no entry at '" + url + "'."); - } - throw new IllegalArgumentException("There is no entry at '" + url + "'."); - } else if (nodeKind == SVNNodeKind.DIR) { - if (log.isErrorEnabled()) { - log.error("The entry at '" + url + "' is a file while a directory was expected."); - } - throw new IllegalArgumentException("The entry at '" + url + "' is a file while a directory was expected."); - } - - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SVNProperties fileProperties = new SVNProperties(); - - repository.getFile(file, -1, fileProperties, baos); - - fileProperties.getStringValue(SVNProperty.REVISION); - - - lastRevision = baos.toString(); - - try { - baos.close(); - } catch (IOException e) { - if (log.isDebugEnabled()) { - log.debug("Can't close stream", e); - } - } + String lastRevision = scmConn.getHeadRevision(login, password); return lastRevision; } - public String getHeadNumberRevision(String address, String login, String password) throws SVNException { + public String getHeadNumberRevision(String address, String login, String password) throws Exception { - ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(login, password); - - DefaultSVNOptions svnOption = new DefaultSVNOptions(); - svnOption.setPropertyValue(SVNProperty.EOL_STYLE, SVNProperty.EOL_STYLE_LF); - - SVNWCClient wcClient = new SVNWCClient(authManager, svnOption); - - SVNInfo info = wcClient.doInfo(SVNURL.parseURIEncoded(address), SVNRevision.HEAD, SVNRevision.HEAD); - - String headRevision = info.getRevision().toString(); + HttpSession session = request.getSession(); + String sessionId = session.getId(); + ScmConnection scmConn = ScmConnectionFactory.createScmConnection(address, sessionId); + String headRevision = scmConn.getHeadNumberRevision(login, password); return headRevision; } @@ -384,22 +273,6 @@ public class ScmWebEditorBaseAction extends BaseAction { } - /** - * Use to delete the checkout temp directory - * - * @param checkoutdir The dir temp directory - */ - public void delTempDirectory(File checkoutdir) { - try { - FileUtils.deleteDirectory(checkoutdir); - } catch (IOException e) { - if (log.isErrorEnabled()) { - log.error("Can't delete temp directory"); - } - } - } - - protected SvnConnection getSvnSession(HttpSession httpSession) { SvnConnection svnSess = (SvnConnection) httpSession.getAttribute(ATTRIBUTE_SVN_SESSION); return svnSess; @@ -496,7 +369,7 @@ public class ScmWebEditorBaseAction extends BaseAction { * * @param file le fichier contenant les propriétés * @return un objet Properties contenant les propriétés du fichier - * @throws IOException + * @throws IOException */ public static Properties loadProperties(String file) throws IOException { Properties properties = new Properties(); @@ -516,8 +389,8 @@ public class ScmWebEditorBaseAction extends BaseAction { * * @param inStream le fichier contenant les propriétés * @return un objet Properties contenant les propriétés du fichier - * @throws IOException - * @throws NullPointerException + * @throws IOException + * @throws NullPointerException */ public static Properties loadProperties(InputStream inStream) throws IOException, NullPointerException { Properties properties = new Properties(); @@ -551,5 +424,10 @@ public class ScmWebEditorBaseAction extends BaseAction { return getScmSession().getPassword(url); } + @Override + public void setServletRequest(HttpServletRequest request) { + this.request = request; + } + } diff --git a/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfig.java b/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfig.java index fa6a489..35e4065 100644 --- a/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfig.java +++ b/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfig.java @@ -62,6 +62,13 @@ public class ScmWebEditorConfig { return config.getOptionAsList(ScmWebEditorConfigOption.EDITABLESFILES.getKey()).getOption(); } + public static String getLocalRepositoriesPath() { return getLocalRepositoriesPath(getConfig()); } + + public static String getLocalRepositoriesPath(ApplicationConfig config) { + String key = ScmWebEditorConfigOption.LOCAL_REPOSITORIES_PATH.getKey(); + return config.getOption(key); + } + public static String getKey() { return getKey(getConfig()); } diff --git a/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfigOption.java b/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfigOption.java index 1e0d3f3..acf9b20 100644 --- a/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfigOption.java +++ b/src/main/java/org/nuiton/scmwebeditor/ScmWebEditorConfigOption.java @@ -28,7 +28,8 @@ public enum ScmWebEditorConfigOption implements ConfigOptionDef { CONFIG_FILE(ApplicationConfig.CONFIG_FILE_NAME, "The file name", "scmwebeditor.properties", String.class, false, false), EDITABLESFILES("editableFiles", "description", "Files types that are editable", String.class, true, true), - COOKIES_PRIVATE_KEY("cookiePrivateKey", "Private key for cookies", null, String.class, true, true); + COOKIES_PRIVATE_KEY("cookiePrivateKey", "Private key for cookies", null, String.class, true, true), + LOCAL_REPOSITORIES_PATH("localRepositoriesPath", "The path where the local repositories will be stored", "/var/local/swe", String.class, false, true); private final String key; diff --git a/src/main/java/org/nuiton/scmwebeditor/SvnConnection.java b/src/main/java/org/nuiton/scmwebeditor/SvnConnection.java index 34621a1..b694338 100644 --- a/src/main/java/org/nuiton/scmwebeditor/SvnConnection.java +++ b/src/main/java/org/nuiton/scmwebeditor/SvnConnection.java @@ -21,41 +21,31 @@ */ package org.nuiton.scmwebeditor; +import com.jgeppert.struts2.jquery.tree.result.TreeNode; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.nuiton.scmwebeditor.actions.ScmWebEditorCommitAction; +import org.nuiton.scmwebeditor.actions.SearchAction; import org.nuiton.util.FileUtil; -import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNProperty; -import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; +import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; -import org.tmatesoft.svn.core.wc.SVNClientManager; -import org.tmatesoft.svn.core.wc.SVNDiffClient; -import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCUtil; +import org.tmatesoft.svn.core.wc.*; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; -public class SvnConnection { +public class SvnConnection extends ScmConnection { private static final Log log = LogFactory.getLog(SvnConnection.class); - /** full svn path */ - protected String addressSvn; - - /** svn path without fileName */ - protected String svnPath; - - /** fileName of modif file */ - protected String fileName; - /** Temp directory for checkout */ protected File checkoutdir; @@ -77,17 +67,69 @@ public class SvnConnection { protected String repositoryId; - public SvnConnection( - String address) throws StringIndexOutOfBoundsException { + public ISVNAuthenticationManager getAuthManager() { + return authManager; + } + + public void setAuthManager(ISVNAuthenticationManager authManager) { + this.authManager = authManager; + } - addressSvn = address; + public File getCheckoutdir() { + return checkoutdir; + } - svnPath = address.substring(0, address.lastIndexOf("/")); + public void createCheckoutdir() throws IOException { + checkoutdir = FileUtil.createTempDirectory("scm_", ""); + } + public void setCheckoutdir(File checkoutdir) { this.checkoutdir = checkoutdir; } + + public SVNClientManager getManager() { + return manager; + } + + public void setManager(SVNClientManager manager) { + this.manager = manager; + } + + public SVNURL getRemoteUrl() { + return remoteUrl; + } + + public void setRemoteUrl(SVNURL remoteUrl) { + this.remoteUrl = remoteUrl; + } + + public DefaultSVNOptions getSvnOption() { + return svnOption; + } + + public void setSvnOption(DefaultSVNOptions svnOption) { this.svnOption = svnOption; } + + public String getScmPath() { return addressScm.substring(0, addressScm.lastIndexOf("/")); } + + public String getRepositoryId() { return repositoryId; } + + public void setRepositoryId(String repositoryId) { this.repositoryId = repositoryId; } + + @Override + public String getFullFileName() { return fileName; } + + + public SvnConnection(String address) throws StringIndexOutOfBoundsException { + + super(address); + + if(log.isDebugEnabled()) { + log.debug("SVN repository"); + } + + scmPath = address.substring(0, address.lastIndexOf("/")); fileName = address.substring(address.lastIndexOf("/") + 1); try { - remoteUrl = SVNURL.parseURIEncoded(svnPath); + remoteUrl = SVNURL.parseURIEncoded(scmPath); } catch (SVNException e) { if (log.isErrorEnabled()) { log.error("Can't parse svnPath", e); @@ -109,7 +151,7 @@ public class SvnConnection { public String getUUID() { String repositoryUUID; try { - SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(addressSvn)); + SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(addressScm)); ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(); repository.setAuthenticationManager(authManager); @@ -128,7 +170,7 @@ public class SvnConnection { public String getSvnRoot() { String repositoryRoot; try { - SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(addressSvn)); + SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(addressScm)); ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(); repository.setAuthenticationManager(authManager); @@ -145,7 +187,7 @@ public class SvnConnection { public void testConnection() throws SVNException { - SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(addressSvn)); + SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(addressScm)); repository.setAuthenticationManager(authManager); @@ -202,72 +244,430 @@ public class SvnConnection { } - public ISVNAuthenticationManager getAuthManager() { - return authManager; - } + @Override + public String search(SearchAction action) { - public void setAuthManager(ISVNAuthenticationManager authManager) { - this.authManager = authManager; - } + String url; - public File getCheckoutdir() { - return checkoutdir; - } + DAVRepositoryFactory.setup(); - public void createCheckoutdir() throws IOException { - checkoutdir = FileUtil.createTempDirectory("scm_", ""); - } + SVNRepository repository; + ISVNAuthenticationManager authManager; - public void setCheckoutdir(File checkoutdir) { - this.checkoutdir = checkoutdir; - } + String name = "anonymous"; + String password = "anonymous"; - public String getFileName() { - return fileName; - } + if (action.getUsername() != null && action.getPw() != null) { + name = action.getUsername(); + password = action.getPw(); + } - public void setFileName(String fileName) { - this.fileName = fileName; - } - public SVNClientManager getManager() { - return manager; - } + if (action.getId().equals("")) { - public void setManager(SVNClientManager manager) { - this.manager = manager; - } + try { + if (log.isDebugEnabled()) { + log.debug("Address svn : " + action.getAddress()); + } - public SVNURL getRemoteUrl() { - return remoteUrl; - } + repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(action.getAddress())); + authManager = SVNWCUtil.createDefaultAuthenticationManager(name, password); + repository.setAuthenticationManager(authManager); - public void setRemoteUrl(SVNURL remoteUrl) { - this.remoteUrl = remoteUrl; + repository.testConnection(); + + } catch (SVNAuthenticationException e) { + if (log.isErrorEnabled()) { + log.error("Can't access to the repository : Auth Problem", e); + } + return SearchAction.AUTH_ERROR; + } catch (SVNException e) { + if (log.isErrorEnabled()) { + log.error("Can't access to the repository", e); + } + action.setError("Can't access to the repository"); + } + + return SearchAction.ROOT; + } else if (action.getId().equals("0")) { + url = action.getAddress(); + } else { + url = action.getId(); + action.setAddress(action.getId()); + } + + + try { + + repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(url)); + authManager = SVNWCUtil.createDefaultAuthenticationManager(name, password); + repository.setAuthenticationManager(authManager); + + if (log.isDebugEnabled()) { + log.debug("Repository Root: " + repository.getRepositoryRoot(true)); + log.debug("Repository UUID: " + repository.getRepositoryUUID(true)); + } + + SVNNodeKind nodeKind = repository.checkPath("", -1); + if (nodeKind == SVNNodeKind.NONE) { + if (log.isWarnEnabled()) { + log.warn("There is no entry at '" + url + "'."); + } + action.setError("There is no entry at '" + url + "'."); + return SearchAction.SUCCESS; + } else if (nodeKind == SVNNodeKind.FILE) { + if (log.isDebugEnabled()) { + log.debug("The entry at '" + url + "' is a file."); + } + TreeNode node = new TreeNode(); + node.setId(url); + node.setTitle(url.substring(url.lastIndexOf("/") + 1)); + node.setState(TreeNode.NODE_STATE_LEAF); + node.setIcon("ui-icon-document"); + action.getNodes().add(node); + return SearchAction.SUCCESS; + } + action.setNumberOfFile(0); + action.listEntries(repository, ""); + if (log.isDebugEnabled()) { + log.debug("Number of file : " + action.getFiles().size()); + } + + + } catch (SVNAuthenticationException authexep) { + if (log.isErrorEnabled()) { + log.error("Can't access to the repository : Auth Problem"); + } + return SearchAction.AUTH_ERROR; + } catch (SVNException svne) { + if (log.isErrorEnabled()) { + log.error("Can't access to the repository"); + } + action.setError("Can't access to the repository"); + } + + if (log.isDebugEnabled()) { + log.debug("Search success"); + } + + return SearchAction.SUCCESS; } - public DefaultSVNOptions getSvnOption() { - return svnOption; + + @Override + public String getHeadRevision(String login, String password) throws SVNException { + + String lastRevision; + + String url = getScmPath(); + String file = getFileName(); + + SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url)); + ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(login, password); + repository.setAuthenticationManager(authManager); + + SVNNodeKind nodeKind = repository.checkPath(file, -1); + + + if (nodeKind == SVNNodeKind.NONE) { + if (log.isErrorEnabled()) { + log.error("There is no entry at '" + url + "'."); + } + throw new IllegalArgumentException("There is no entry at '" + url + "'."); + } else if (nodeKind == SVNNodeKind.DIR) { + if (log.isErrorEnabled()) { + log.error("The entry at '" + url + "' is a file while a directory was expected."); + } + throw new IllegalArgumentException("The entry at '" + url + "' is a file while a directory was expected."); + } + + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + SVNProperties fileProperties = new SVNProperties(); + + repository.getFile(file, -1, fileProperties, baos); + + fileProperties.getStringValue(SVNProperty.REVISION); + + + lastRevision = baos.toString(); + + try { + baos.close(); + } catch (IOException e) { + if (log.isDebugEnabled()) { + log.debug("Can't close stream", e); + } + } + + return lastRevision; } - public void setSvnOption(DefaultSVNOptions svnOption) { - this.svnOption = svnOption; + + @Override + public String getHeadNumberRevision(String login, String password) throws SVNException { + + ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(login, password); + + DefaultSVNOptions svnOption = new DefaultSVNOptions(); + svnOption.setPropertyValue(SVNProperty.EOL_STYLE, SVNProperty.EOL_STYLE_LF); + + SVNWCClient wcClient = new SVNWCClient(authManager, svnOption); + + SVNInfo info = wcClient.doInfo(SVNURL.parseURIEncoded(addressScm), SVNRevision.HEAD, SVNRevision.HEAD); + + String headRevision = info.getRevision().toString(); + + return headRevision; } - public String getSvnPath() { - return svnPath; + + @Override + public String commit(ScmWebEditorCommitAction action, String login, String password, + String address, String commitMessage) { + + if (log.isDebugEnabled()) { + log.debug("Entering SVN commit"); + } + + try { + createCheckoutdir(); + } catch (IOException e1) { + if (log.isErrorEnabled()) { + log.error("Can't create checkoutDir", e1); + } + return action.ERROR; + } + + + // before the commit, we have to checkout the repository + try { + checkout(checkoutdir); + } catch (SVNAuthenticationException authexep) { + action.getRequest().setAttribute(action.PARAMETER_ADDRESS, address); + + // if svn authentication failed user is redirected on login page + if (log.isDebugEnabled()) { + log.debug("Private SCM on reading " + getRemoteUrl()); + } + // we delete the temporary directory + delTempDirectory(checkoutdir); + + action.setUsername(null); + action.setPw(null); + action.getScmSession().delScmUser(getUUID()); + + return action.LOGIN; + + } catch (SVNException e) { + if (log.isErrorEnabled()) { + log.error("Can't checkout the file", e); + } + // deleting the temporary directory + delTempDirectory(checkoutdir); + return action.ERROR_PATH; + } + + + File checkOutFile = new File(checkoutdir, getFileName()); + + + action.setLastText(action.getNewText()); + + try { + String originalText = FileUtils.readFileToString(checkOutFile); + + action.setOrigText(originalText); + + + } catch (FileNotFoundException ee) { + /* fichier non trouve, on redirige vers BadFileRedirect.jsp + * après avoir supprimé le repertoire temporaire + */ + delTempDirectory(checkoutdir); + + return action.ERROR; + } catch (IOException e) { + log.error("Can't find the checkout file", e); + } + + + /* + * Diff + */ + if (!action.getForce()) { + try { + + if (isDifferent(action.getOrigText())) { + ByteArrayOutputStream differents = getDiff(action.getNewText()); + + if (differents.size() > 0) { + action.setDiff(differents.toString()); + + String diff = action.getDiff(); + + action.setDiff(diff.substring(diff.indexOf("@@"))); + delTempDirectory(checkoutdir); + try { + action.setHeadCommiter(action.getHeadcommiter(address, login, password)); + } catch (SVNException e) { + log.error("Can't get head commiter", e); + } + return action.FILE_MODIFY; + } + } + + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can't do diff on file, IO error", e); + } + } + } + + + /* + * Commit process + */ +// File pathToFile = new File(checkoutdir, svnConn.getFileName()); + + SVNCommitClient commitClient = new SVNCommitClient(manager, svnOption); + + File pathToFile = new File(checkoutdir, fileName); + + + try { + FileUtil.writeString(pathToFile, action.getNewText(), "UTF-8"); + } catch (IOException e1) { + delTempDirectory(checkoutdir); + return action.ERROR; + } + + + File[] tabFile = new File[1]; + tabFile[0] = pathToFile; + + + try { + if (log.isDebugEnabled()) { + log.debug("Try to commit"); + } + commitClient.doCommit(tabFile, false, "From scmwebeditor -- " + commitMessage, null, null, false, false, SVNDepth.FILES); + } catch (SVNAuthenticationException authexep) { + if (log.isErrorEnabled()) { + log.error("AUTH FAIL", authexep); + } + + // if authentication failed edition page is reloaded form user's relogin + + action.setOrigText(action.getNewText()); + action.setBadLogin(true); + + // deleteing temporary directory + delTempDirectory(checkoutdir); + action.setUsername(null); + action.setPw(null); + // deleting the session value + action.getScmSession().delScmUser(getUUID()); + return action.LOGIN; + } catch (SVNException e) { + if (log.isErrorEnabled()) { + log.error("SVN FAIL", e); + } + // deleteing temporary directory + delTempDirectory(checkoutdir); + return action.ERROR; + } + + + if (checkoutdir != null) { + // deleteing temporary directory + delTempDirectory(checkoutdir); + } + + if (log.isDebugEnabled()) { + log.debug("End of commit"); + } + // deleteing temporary directory + delTempDirectory(checkoutdir); + + if (log.isInfoEnabled()) { + log.info(login + " with IP " + action.getRequest().getRemoteAddr() + " commit the file " + + address + " with message : " + commitMessage); + } + + try { + action.setNumRevision(action.getHeadNumberRevision(address, login, password)); + } catch (SVNException e) { + action.setNumRevision(null); + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Unknown error", e); + } + action.setNumRevision(null); + } + + return action.SUCCESS; } - public void setSvnPath(String svnPath) { - this.svnPath = svnPath; + + /** + * @param checkoutdir + * @param numVersion + * @throws SVNException + */ + public void checkout(File checkoutdir, String numVersion) throws SVNException { + + SVNUpdateClient upclient = new SVNUpdateClient(manager, svnOption); + + if (log.isDebugEnabled()) { + log.debug("Do Checkout of " + remoteUrl); + } + try { + upclient.doCheckout(remoteUrl, checkoutdir, + SVNRevision.create(Long.parseLong(numVersion)), SVNRevision.create(Long.parseLong(numVersion)), SVNDepth.FILES, false); + } catch (NumberFormatException e) { + if (log.isErrorEnabled()) { + log.error("The number version is not valid."); + } + upclient.doCheckout(remoteUrl, checkoutdir, + SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.FILES, false); + + } + + } - public String getRepositoryId() { - return repositoryId; + /** + * @param checkoutdir + * @throws SVNException + */ + public void checkout(File checkoutdir) throws SVNException { + + SVNUpdateClient upclient = new SVNUpdateClient(manager, svnOption); + + if (log.isDebugEnabled()) { + log.debug("Do Checkout of " + remoteUrl); + } + + upclient.doCheckout(remoteUrl, checkoutdir, + SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.FILES, false); } - public void setRepositoryId(String repositoryId) { - this.repositoryId = repositoryId; + + /** + * Use to delete the checkout temp directory + * + * @param checkoutdir The dir temp directory + */ + public void delTempDirectory(File checkoutdir) { + try { + FileUtils.deleteDirectory(checkoutdir); + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can't delete temp directory"); + } + } } diff --git a/src/main/java/org/nuiton/scmwebeditor/SweSessionListener.java b/src/main/java/org/nuiton/scmwebeditor/SweSessionListener.java new file mode 100644 index 0000000..bd258ef --- /dev/null +++ b/src/main/java/org/nuiton/scmwebeditor/SweSessionListener.java @@ -0,0 +1,42 @@ +package org.nuiton.scmwebeditor; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; +import java.io.File; +import java.io.IOException; + +public class SweSessionListener implements HttpSessionListener { + + private static final Log log = LogFactory.getLog(SweSessionListener.class); + + @Override + public void sessionCreated(HttpSessionEvent httpSessionEvent) { + + } + + @Override + public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { + + String sessionId = httpSessionEvent.getSession().getId(); + + String localReposPath = ScmWebEditorConfig.getLocalRepositoriesPath(); + + File localDirectory = new File(localReposPath + "/" + sessionId); + + try { + FileUtils.deleteDirectory(localDirectory); + + if (log.isDebugEnabled()) { + log.debug("Deleted directory " + localDirectory.getAbsolutePath()); + } + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Can not delete directory " + localDirectory.getAbsolutePath(), e); + } + } + } +} diff --git a/src/main/java/org/nuiton/scmwebeditor/actions/ResetAction.java b/src/main/java/org/nuiton/scmwebeditor/actions/ResetAction.java index d924320..e4fc659 100644 --- a/src/main/java/org/nuiton/scmwebeditor/actions/ResetAction.java +++ b/src/main/java/org/nuiton/scmwebeditor/actions/ResetAction.java @@ -124,6 +124,10 @@ public class ResetAction extends ScmWebEditorBaseAction { } error = ERROR_PATH; return ERROR_PATH; + } catch (Exception e) { + if (log.isErrorEnabled()) { + log.error("Unknown error", e); + } } return SUCCESS; diff --git a/src/main/java/org/nuiton/scmwebeditor/actions/SaveAction.java b/src/main/java/org/nuiton/scmwebeditor/actions/SaveAction.java index 7ed27bd..afdc8d0 100644 --- a/src/main/java/org/nuiton/scmwebeditor/actions/SaveAction.java +++ b/src/main/java/org/nuiton/scmwebeditor/actions/SaveAction.java @@ -37,6 +37,8 @@ public class SaveAction extends ScmWebEditorCommitAction { protected Date date; + protected static final String USELESS_SAVE = "uselessSave"; + public String getResult() { return result; } @@ -58,7 +60,7 @@ public class SaveAction extends ScmWebEditorCommitAction { if (origText.equals(newText)) { - result = "uselessSave"; + result = USELESS_SAVE; return SUCCESS; } diff --git a/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorCommitAction.java b/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorCommitAction.java index 3aac64f..609c994 100644 --- a/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorCommitAction.java +++ b/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorCommitAction.java @@ -21,7 +21,6 @@ */ package org.nuiton.scmwebeditor.actions; -import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.shiro.codec.Base64; @@ -29,9 +28,7 @@ import org.apache.shiro.crypto.BlowfishCipherService; import org.apache.struts2.interceptor.ServletRequestAware; import org.apache.struts2.interceptor.ServletResponseAware; import org.nuiton.jrst.JRST; -import org.nuiton.scmwebeditor.ScmWebEditorBaseAction; -import org.nuiton.scmwebeditor.ScmWebEditorConfig; -import org.nuiton.scmwebeditor.SvnConnection; +import org.nuiton.scmwebeditor.*; import org.nuiton.util.FileUtil; import org.tmatesoft.svn.core.SVNAuthenticationException; import org.tmatesoft.svn.core.SVNDepth; @@ -41,9 +38,9 @@ import org.tmatesoft.svn.core.wc.SVNCommitClient; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; @@ -185,6 +182,8 @@ public class ScmWebEditorCommitAction extends ScmWebEditorBaseAction implements return headCommiter; } + public void setHeadCommiter(String headCommiter) { this.headCommiter = headCommiter; } + public boolean isSaveCookie() { return saveCookie; } @@ -201,6 +200,14 @@ public class ScmWebEditorCommitAction extends ScmWebEditorBaseAction implements this.mimeType = mimeType; } + public HttpServletRequest getRequest() { return request; } + + public boolean getForce() { return force; } + + public void setDiff(String diff) { this.diff = diff; } + + public void setBadLogin(boolean badLogin) { this.badLogin = badLogin; } + public boolean isBadLogin() { return badLogin; @@ -236,25 +243,17 @@ public class ScmWebEditorCommitAction extends ScmWebEditorBaseAction implements } } - - SvnConnection svnConn; - - - try { - svnConn = new SvnConnection(address); - } catch (StringIndexOutOfBoundsException e) { - if (log.isDebugEnabled()) { - log.debug("Parameter is not valid ", e); - } - return "error"; - } + // connection to the repository + HttpSession session = request.getSession(); + String sessionId = session.getId(); + ScmConnection scmConn = ScmConnectionFactory.createScmConnection(address, sessionId); String login = getUsername(); String password = getPw(); - //Si le repo n'est pas protege en ecriture on recupere sont UUID - String repositoryUUID = svnConn.getUUID(); + // if the repository is not protected for writing, we get its UUID + String repositoryUUID = scmConn.getUUID(); if (repositoryUUID == null) { repositoryUUID = address; } @@ -313,178 +312,16 @@ public class ScmWebEditorCommitAction extends ScmWebEditorBaseAction implements } - svnConn.updateAuthentication(login, password); - - File checkoutdir; - try { - svnConn.createCheckoutdir(); - } catch (IOException e1) { - if (log.isErrorEnabled()) { - log.error("Can't create checkoutDir", e1); - } - return "error"; + // FIXME gérer l'identification avec Git + if (scmConn instanceof SvnConnection) { + ((SvnConnection) scmConn).updateAuthentication(login, password); } - checkoutdir = svnConn.getCheckoutdir(); + String returnCode; - // Avant le commit, il faut checkout le repertoire - try { - checkout(svnConn, checkoutdir); - } catch (SVNAuthenticationException authexep) { - request.setAttribute(PARAMETER_ADDRESS, address); - - // if svn authentication failed user is redirected on login page - if (log.isDebugEnabled()) { - log.debug("Private SCM on reading " + svnConn.getRemoteUrl()); - } - //On supprime le repertoire temporaire - delTempDirectory(checkoutdir); - - username = null; - pw = null; - getScmSession().delScmUser(repositoryUUID); - - return LOGIN; - - } catch (SVNException e) { - if (log.isErrorEnabled()) { - log.error("Can't checkout the file", e); - } - //Suppression du repertoire temporaire - delTempDirectory(checkoutdir); - return ERROR_PATH; - } - - - File checkOutFile = new File(checkoutdir, svnConn.getFileName()); - - - lastText = newText; - - try { - String originalText = FileUtils.readFileToString(checkOutFile); - - origText = originalText; - - - } catch (FileNotFoundException ee) { - /* fichier non trouve, on redirige vers BadFileRedirect.jsp - * après avoir supprimé le repertoire temporaire - */ - delTempDirectory(checkoutdir); - - return ERROR; - } catch (IOException e) { - log.error("Can't find the checkout file", e); - } - - - /* - * Diff - */ - if (!force) { - try { - - if (svnConn.isDifferent(origText)) { - ByteArrayOutputStream differents = svnConn.getDiff(newText); - - if (differents.size() > 0) { - diff = differents.toString(); - diff = diff.substring(diff.indexOf("@@")); - delTempDirectory(checkoutdir); - try { - headCommiter = getHeadcommiter(address, login, password); - } catch (SVNException e) { - log.error("Can't get head commiter", e); - } - return FILE_MODIFY; - } - } - - } catch (IOException e) { - if (log.isErrorEnabled()) { - log.error("Can't do diff on file, IO error", e); - } - } - } - - - /* - * Commit process - */ -// File pathToFile = new File(checkoutdir, svnConn.getFileName()); - - SVNCommitClient commitClient = new SVNCommitClient(svnConn.getManager(), svnConn.getSvnOption()); - - File pathToFile = new File(checkoutdir, svnConn.getFileName()); - - - try { - FileUtil.writeString(pathToFile, newText, "UTF-8"); - } catch (IOException e1) { - delTempDirectory(checkoutdir); - return ERROR; - } - - - File[] tabFile = new File[1]; - tabFile[0] = pathToFile; - - - try { - if (log.isDebugEnabled()) { - log.debug("Try to commit"); - } - commitClient.doCommit(tabFile, false, "From scmwebeditor -- " + commitMessage, null, null, false, false, SVNDepth.FILES); - } catch (SVNAuthenticationException authexep) { - if (log.isErrorEnabled()) { - log.error("AUTH FAIL", authexep); - } - - // if authentication failed edition page is reload form user's relogin - - origText = newText; - badLogin = true; - - //Suppression du repertoire temporaire - delTempDirectory(checkoutdir); - username = null; - pw = null; - //on supprime la valeur stocke en session - getScmSession().delScmUser(repositoryUUID); - return LOGIN; - } catch (SVNException e) { - if (log.isErrorEnabled()) { - log.error("SVN FAIL", e); - } - //Suppression du repertoire temporaire - delTempDirectory(checkoutdir); - return "error"; - } - - - if (checkoutdir != null) { - //Suppression du repertoire temporaire - delTempDirectory(checkoutdir); - } - - if (log.isDebugEnabled()) { - log.debug("End of commit"); - } - //Suppression du repertoire temporaire - delTempDirectory(checkoutdir); - - if (log.isInfoEnabled()) { - log.info(login + " with IP " + request.getRemoteAddr() + " commit the file " + address + " with message : " + commitMessage); - } - - try { - numRevision = getHeadNumberRevision(address, login, password); - } catch (SVNException e) { - numRevision = null; - } + returnCode = scmConn.commit(this, login, password, address, commitMessage); - return SUCCESS; + return returnCode; } diff --git a/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorMainAction.java b/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorMainAction.java index e907e82..1b35aa6 100644 --- a/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorMainAction.java +++ b/src/main/java/org/nuiton/scmwebeditor/actions/ScmWebEditorMainAction.java @@ -27,20 +27,18 @@ import org.apache.shiro.codec.Base64; import org.apache.shiro.crypto.BlowfishCipherService; import org.apache.struts2.interceptor.ServletRequestAware; import org.apache.struts2.interceptor.ServletResponseAware; -import org.nuiton.scmwebeditor.ScmWebEditorBaseAction; -import org.nuiton.scmwebeditor.ScmWebEditorConfig; -import org.nuiton.scmwebeditor.SvnConnection; +import org.nuiton.scmwebeditor.*; import org.tmatesoft.svn.core.SVNAuthenticationException; import org.tmatesoft.svn.core.SVNException; import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.LinkedList; -public class ScmWebEditorMainAction extends ScmWebEditorBaseAction implements ServletRequestAware, ServletResponseAware { +public class ScmWebEditorMainAction extends ScmWebEditorBaseAction implements ServletResponseAware { private static final long serialVersionUID = 8361035067228171624L; @@ -73,8 +71,6 @@ public class ScmWebEditorMainAction extends ScmWebEditorBaseAction implements Se protected String pw; - protected transient HttpServletRequest request; - protected transient HttpServletResponse response; protected String repositoryId; @@ -241,25 +237,16 @@ public class ScmWebEditorMainAction extends ScmWebEditorBaseAction implements Se // } - SvnConnection svnConn; + HttpSession session = request.getSession(); + String sessionId = session.getId(); + ScmConnection scmConn = ScmConnectionFactory.createScmConnection(address, sessionId); - - try { - svnConn = new SvnConnection(address); - } catch (StringIndexOutOfBoundsException e) { - if (log.isDebugEnabled()) { - log.debug("Parameter is not valid ", e); - } - return ERROR_PATH; - } - - - format = svnConn.getFileName().substring(svnConn.getFileName().lastIndexOf(".") + 1); + format = scmConn.getFileName().substring(scmConn.getFileName().lastIndexOf(".") + 1); String originalText; //Si le repo n'est pas protege en ecriture on recupere sont UUID - String repositoryUUID = svnConn.getUUID(); + String repositoryUUID = scmConn.getUUID(); if (repositoryUUID == null) { repositoryUUID = address; } @@ -327,8 +314,10 @@ public class ScmWebEditorMainAction extends ScmWebEditorBaseAction implements Se getScmSession().addScmUser(repositoryUUID, username, pw); } - - svnConn.updateAuthentication(username, pw); + // FIXME gestion de l'authentification sous Git + if (scmConn instanceof SvnConnection) { + ((SvnConnection) scmConn).updateAuthentication(username, pw); + } /* @@ -376,12 +365,22 @@ public class ScmWebEditorMainAction extends ScmWebEditorBaseAction implements Se log.debug("SVN error debug", e); } return ERROR_PATH; + } catch (IOException e) { + if (log.isDebugEnabled()) { + log.debug("Can't read local file", e); + } + return ERROR_PATH; + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Unknown error", e); + } + return ERROR_PATH; } mimeType = null; try { - mimeType = getMimeType(originalText, svnConn.getFileName()); + mimeType = getMimeType(originalText, scmConn.getFullFileName()); } catch (IOException e) { if (log.isErrorEnabled()) { log.error("Can't get MimeType, problem when reading file", e); @@ -428,11 +427,6 @@ public class ScmWebEditorMainAction extends ScmWebEditorBaseAction implements Se } - @Override - public void setServletRequest(HttpServletRequest request) { - this.request = request; - } - @Override public void setServletResponse(HttpServletResponse response) { diff --git a/src/main/java/org/nuiton/scmwebeditor/actions/SearchAction.java b/src/main/java/org/nuiton/scmwebeditor/actions/SearchAction.java index f69f70c..b0f8dc2 100644 --- a/src/main/java/org/nuiton/scmwebeditor/actions/SearchAction.java +++ b/src/main/java/org/nuiton/scmwebeditor/actions/SearchAction.java @@ -24,25 +24,16 @@ package org.nuiton.scmwebeditor.actions; import com.jgeppert.struts2.jquery.tree.result.TreeNode; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.nuiton.scmwebeditor.ScmWebEditorBaseAction; -import org.tmatesoft.svn.core.SVNAuthenticationException; +import org.nuiton.scmwebeditor.*; import org.tmatesoft.svn.core.SVNDirEntry; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; -import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; -import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; import org.tmatesoft.svn.core.io.SVNRepository; -import org.tmatesoft.svn.core.io.SVNRepositoryFactory; -import org.tmatesoft.svn.core.wc.SVNWCUtil; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.*; import java.util.Map.Entry; @@ -52,6 +43,8 @@ public class SearchAction extends ScmWebEditorBaseAction { private static final Log log = LogFactory.getLog(SearchAction.class); + public static final String ROOT = "root"; + protected String address; protected List<String> files; @@ -74,199 +67,103 @@ public class SearchAction extends ScmWebEditorBaseAction { protected String id = ""; - public String getError() { - return error; - } + protected List<String> branches; - public String getList() { - return list; - } + protected String selectedBranch; - public void setList(String list) { - this.list = list; - } + protected String headBranchName; - public List<String> getFiles() { - return files; - } + public String getError() { return error; } - public Map<String, String> getDirectories() { - return directories; - } + public void setError(String error) { this.error = error; } - public String getInfo() { - return info; - } + public String getList() { return list; } - public String getAddress() { - return address; - } + public void setList(String list) { this.list = list; } - public void setAddress(String address) { - this.address = address; - } + public List<String> getFiles() { return files; } - public String getUsername() { - return username; - } + public void setFiles(List<String> files) { this.files = files; } - public void setUsername(String username) { - this.username = username; - } + public Map<String, String> getDirectories() { return directories; } - public String getPw() { - return pw; - } + public void setDirectories(Map<String, String> directories) { this.directories = directories; } - public void setPw(String pw) { - this.pw = pw; - } + public String getInfo() { return info; } - public int getNumberOfFile() { - return numberOfFile; - } + public String getAddress() { return address; } - public String search() { + public void setNumberOfFile(int numberOfFile) { this.numberOfFile = numberOfFile; } - if (log.isDebugEnabled()) { - log.debug("Enter in search action"); - } - - - DAVRepositoryFactory.setup(); + public void setAddress(String address) { this.address = address; } - String url; + public String getUsername() { return username; } + public void setUsername(String username) { this.username = username; } - String name = "anonymous"; - String password = "anonymous"; + public String getPw() { return pw; } - if (username != null && pw != null) { - name = username; - password = pw; - } - - - if (address.endsWith("/")) { - address = address.substring(0, address.length() - 1); - } + public void setPw(String pw) { this.pw = pw; } + public String getId() { return id; } - SVNRepository repository; - ISVNAuthenticationManager authManager; + public int getNumberOfFile() { return numberOfFile; } + public List<String> getBranches() { return branches; } - if (id.equals("")) { + public String getHeadBranchName() { return headBranchName; } - try { - if (log.isDebugEnabled()) { - log.debug("Address svn : " + address); - } + public String getSelectedBranch() { return selectedBranch; } - repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(address)); - authManager = SVNWCUtil.createDefaultAuthenticationManager(name, password); - repository.setAuthenticationManager(authManager); + public void setSelectedBranch(String selectedBranch) { this.selectedBranch = selectedBranch; } - repository.testConnection(); + public void setHeadBranchName(String headBranchName) { this.headBranchName = headBranchName; } - } catch (SVNAuthenticationException e) { - if (log.isErrorEnabled()) { - log.error("Can't access to the repository : Auth Problem", e); - } - return "authError"; - } catch (SVNException e) { - if (log.isErrorEnabled()) { - log.error("Can't access to the repository", e); - } - error = "Can't access to the repository"; - } + public String search() { - return "root"; - } else if (id.equals("0")) { - url = address; - } else { - url = id; - address = id; + if (log.isDebugEnabled()) { + log.debug("Enter in search action"); } + if (address.endsWith("/")) { + address = address.substring(0, address.length() - 1); + } - try { - - repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(url)); - authManager = SVNWCUtil.createDefaultAuthenticationManager(name, password); - repository.setAuthenticationManager(authManager); - - if (log.isDebugEnabled()) { - log.debug("Repository Root: " + repository.getRepositoryRoot(true)); - log.debug("Repository UUID: " + repository.getRepositoryUUID(true)); - } - - SVNNodeKind nodeKind = repository.checkPath("", -1); - if (nodeKind == SVNNodeKind.NONE) { - if (log.isWarnEnabled()) { - log.warn("There is no entry at '" + url + "'."); - } - error = "There is no entry at '" + url + "'."; - return SUCCESS; - } else if (nodeKind == SVNNodeKind.FILE) { - if (log.isDebugEnabled()) { - log.debug("The entry at '" + url + "' is a file."); - } - TreeNode node = new TreeNode(); - node.setId(url); - node.setTitle(url.substring(url.lastIndexOf("/") + 1)); - node.setState(TreeNode.NODE_STATE_LEAF); - node.setIcon("ui-icon-document"); - nodes.add(node); - return SUCCESS; - } - numberOfFile = 0; - listEntries(repository, ""); - if (log.isDebugEnabled()) { - log.debug("Number of file : " + files.size()); - } - - - for (String file : files) { - TreeNode node = new TreeNode(); - node.setId(file); - node.setTitle(file.substring(file.lastIndexOf("/") + 1)); - node.setState(TreeNode.NODE_STATE_LEAF); - node.setIcon("ui-icon-document"); - nodes.add(node); - } + // connection to the repository + HttpSession session = request.getSession(); + String sessionId = session.getId(); + ScmConnection scmConn = ScmConnectionFactory.createScmConnection(address, sessionId); + // getting the files and directories + String returnCode = scmConn.search(this); - Iterator<Entry<String, String>> iter1 = directories.entrySet().iterator(); - while (iter1.hasNext()) { - Map.Entry<String, String> ent = (Map.Entry<String, String>) iter1.next(); + if (returnCode != SUCCESS) { + return returnCode; + } - String value = ent.getValue(); + // building the tree - TreeNode node = new TreeNode(); - node.setId(value); - node.setTitle(value.substring(value.lastIndexOf("/"))); - nodes.add(node); + for (String file : files) { + TreeNode node = new TreeNode(); + node.setId(file); + node.setTitle(file.substring(file.lastIndexOf("/") + 1)); + node.setState(TreeNode.NODE_STATE_LEAF); + node.setIcon("ui-icon-document"); + nodes.add(node); + } - //Traitement - } + Iterator<Entry<String, String>> iter1 = directories.entrySet().iterator(); + while (iter1.hasNext()) { + Map.Entry<String, String> ent = (Map.Entry<String, String>) iter1.next(); + String value = ent.getValue(); - } catch (SVNAuthenticationException authexep) { - if (log.isErrorEnabled()) { - log.error("Can't access to the repository : Auth Problem"); - } - return "authError"; - } catch (SVNException svne) { - if (log.isErrorEnabled()) { - log.error("Can't access to the repository"); - } - error = "Can't access to the repository"; - } + TreeNode node = new TreeNode(); + node.setId(value); - if (log.isDebugEnabled()) { - log.debug("Search success"); + node.setTitle(value.substring(value.lastIndexOf("/"))); + nodes.add(node); } return SUCCESS; @@ -311,6 +208,25 @@ public class SearchAction extends ScmWebEditorBaseAction { } + public String getBranchesJSON() { + + String json = "{\"branches\":["; + branches = GitConnection.getBranches(address); + + // creating the JSON String + for (String branch : branches) { + json += "{\"name\":\"" + branch + "\"},"; + } + + // we don't take the last coma + json = json.substring(0, json.length() - 2); + + json += "]}"; + + return json; + } + + public String getJSON() { return search(); } diff --git a/src/main/java/org/nuiton/scmwebeditor/actions/UploadAction.java b/src/main/java/org/nuiton/scmwebeditor/actions/UploadAction.java index 7f9f8b6..e1d1215 100644 --- a/src/main/java/org/nuiton/scmwebeditor/actions/UploadAction.java +++ b/src/main/java/org/nuiton/scmwebeditor/actions/UploadAction.java @@ -148,7 +148,7 @@ public class UploadAction extends ScmWebEditorBaseAction implements ServletReque SvnConnection svnConnection = new SvnConnection(address); svnRoot = svnConnection.getSvnRoot(); - fileRoot = svnConnection.getSvnPath(); + fileRoot = svnConnection.getScmPath(); if (svnRoot == null) { svnRoot = fileRoot; @@ -247,14 +247,14 @@ public class UploadAction extends ScmWebEditorBaseAction implements ServletReque log.debug("Private SCM on reading " + svnConn.getRemoteUrl()); } //On supprime le repertoire temporaire - delTempDirectory(checkoutDir); + svnConn.delTempDirectory(checkoutDir); //redirect to a login page error = true; return ERROR; } catch (SVNException e) { //Suppression du repertoire temporaire - delTempDirectory(checkoutDir); + svnConn.delTempDirectory(checkoutDir); error = true; return ERROR; } @@ -307,7 +307,7 @@ public class UploadAction extends ScmWebEditorBaseAction implements ServletReque } error = true; //Suppression du repertoire temporaire - delTempDirectory(checkoutDir); + svnConn.delTempDirectory(checkoutDir); return ERROR; } @@ -327,7 +327,7 @@ public class UploadAction extends ScmWebEditorBaseAction implements ServletReque } badLogin = true; //Suppression du repertoire temporaire - delTempDirectory(checkoutDir); + svnConn.delTempDirectory(checkoutDir); getScmSession().delScmUser(repositoryUUID); return LOGIN; } catch (SVNException e) { @@ -336,13 +336,13 @@ public class UploadAction extends ScmWebEditorBaseAction implements ServletReque } error = true; //Suppression du repertoire temporaire - delTempDirectory(checkoutDir); + svnConn.delTempDirectory(checkoutDir); return ERROR; } //Suppression du repertoire temporaire - delTempDirectory(checkoutDir); + svnConn.delTempDirectory(checkoutDir); if (log.isDebugEnabled()) { log.debug("File upload successful"); diff --git a/src/main/resources/i18n/scmwebeditor_en_GB.properties b/src/main/resources/i18n/scmwebeditor_en_GB.properties index d71aa07..e9bae11 100644 --- a/src/main/resources/i18n/scmwebeditor_en_GB.properties +++ b/src/main/resources/i18n/scmwebeditor_en_GB.properties @@ -27,10 +27,13 @@ scm.modificationViewer.betterUseJavascript=For a better use of SCMWebEditor plea scm.modificationViewer.noJavascript=Javascript is not activated. You can't only use Save and Quit or upload button. scm.mustBeLog=You must be login to see this repository. scm.no=No +scm.outConnection.branches=List of branches scm.outConnection.enterRepo=Please enter your repository address. +scm.outConnection.headBranch=Current branch\: scm.outConnection.noJavascript=Javascript is not activated. Please activate it for a fully use of ScmWebEditor. scm.outConnection.scmPath=SCM path \: scm.outConnection.search=Search +scm.outConnection.selectBranch=Select a branch\: scm.password=Password scm.passwordTitle=Repository password scm.pathError=Path error diff --git a/src/main/resources/i18n/scmwebeditor_fr_FR.properties b/src/main/resources/i18n/scmwebeditor_fr_FR.properties index 10d6205..f7430b4 100644 --- a/src/main/resources/i18n/scmwebeditor_fr_FR.properties +++ b/src/main/resources/i18n/scmwebeditor_fr_FR.properties @@ -27,10 +27,13 @@ scm.modificationViewer.betterUseJavascript=Activer Javascript pour accéder à t scm.modificationViewer.noJavascript=Javascript est désactivé. Vous pouvez seulement utiliser les boutons sauvegarder et quitter, quitter ou ajouter un fichier. scm.mustBeLog=Vous devez vous identifier pour parcourir ce dépôt. scm.no=Non +scm.outConnection.branches=Liste des branches scm.outConnection.enterRepo=Entrez l'adresse de votre dépôt. +scm.outConnection.headBranch=Branche courante \: scm.outConnection.noJavascript=Javascript n'est pas activé. Certaines fonctions ne seront pas accessibles. scm.outConnection.scmPath=Répertoire SCM \: scm.outConnection.search=Chercher +scm.outConnection.selectBranch=Sélectionner une branche \: scm.password=Mot de passe scm.passwordTitle=Mot de passe du dépôt scm.pathError=Erreur dans le chemin diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index 285d047..d442781 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -20,7 +20,8 @@ # #L% ### # Global logging configuration -log4j.rootLogger=INFO, stdout +log4j.rootLogger=DEBUG, stdout, fileout +#log4j.rootLogger=INFO, stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender @@ -28,26 +29,26 @@ log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy/MM/dd hh:mm:ss} %5p (%F:%L) %m%n # File output... -#log4j.appender.fileout=org.apache.log4j.FileAppender -#log4j.appender.fileout.File=jtimer.log -#log4j.appender.fileout.Append=true -#log4j.appender.fileout.Threshold=DEBUG -#log4j.appender.fileout.layout=org.apache.log4j.PatternLayout -#log4j.appender.fileout.layout.ConversionPattern=%5p (%F:%L) %m%n +log4j.appender.fileout=org.apache.log4j.FileAppender +log4j.appender.fileout.File=jtimer.log +log4j.appender.fileout.Append=true +log4j.appender.fileout.Threshold=DEBUG +log4j.appender.fileout.layout=org.apache.log4j.PatternLayout +log4j.appender.fileout.layout.ConversionPattern=%5p (%F:%L) %m%n # Rolling appender -#log4j.appender.rolling=org.apache.log4j.RollingFileAppender -#log4j.appender.rolling.File=jtimer.log -#log4j.appender.rolling.MaxFileSize=100KB -#log4j.appender.rolling.Append=true -#log4j.appender.rolling.MaxBackupIndex=30 -#log4j.appender.rolling.Threshold=INFO -#log4j.appender.rolling.layout=org.apache.log4j.PatternLayout -#log4j.appender.rolling.layout.ConversionPattern=%d{yyyy/MM/dd hh:mm:ss} %5p (%F:%L) %m%n +log4j.appender.rolling=org.apache.log4j.RollingFileAppender +log4j.appender.rolling.File=jtimer.log +log4j.appender.rolling.MaxFileSize=100KB +log4j.appender.rolling.Append=true +log4j.appender.rolling.MaxBackupIndex=30 +log4j.appender.rolling.Threshold=INFO +log4j.appender.rolling.layout=org.apache.log4j.PatternLayout +log4j.appender.rolling.layout.ConversionPattern=%d{yyyy/MM/dd hh:mm:ss} %5p (%F:%L) %m%n # package level -#log4j.logger.org.nuiton.scmwebeditor.actions.SaveAction=DEBUG +log4j.logger.org.nuiton.scmwebeditor.actions.SaveAction=DEBUG -#log4j.logger.org.chorem.jtimer=DEBUG -#log4j.logger.org.chorem.jtimer.ws=DEBUG -#log4j.logger.org.chorem.jtimer.ui.report=DEBUG +log4j.logger.org.chorem.jtimer=DEBUG +log4j.logger.org.chorem.jtimer.ws=DEBUG +log4j.logger.org.chorem.jtimer.ui.report=DEBUG diff --git a/src/main/resources/scmwebeditor.properties b/src/main/resources/scmwebeditor.properties index 76b8f36..e6bd17b 100644 --- a/src/main/resources/scmwebeditor.properties +++ b/src/main/resources/scmwebeditor.properties @@ -21,3 +21,4 @@ ### editableFiles=text,xml,javascript,sh,x-tex,x-java cookiePrivateKey=ZvcCyhfRTVZoQz3B/IpYdw== +localRepositoriesPath=/var/local/swe diff --git a/src/main/resources/struts.xml b/src/main/resources/struts.xml index 8d25751..a6e9051 100644 --- a/src/main/resources/struts.xml +++ b/src/main/resources/struts.xml @@ -116,12 +116,19 @@ <action name="search" class="org.nuiton.scmwebeditor.actions.SearchAction" method="search"> <param name="address"/> - <result name="root" >/WEB-INF/content/search.jsp</result> + <result name="root">/WEB-INF/content/search.jsp</result> <result name="success" type="json"> <param name="root">nodes</param> </result> <result name="authError" >/WEB-INF/content/loginSearch.jsp</result> </action> + + <action name="getBranches" class="org.nuiton.scmwebeditor.actions.SearchAction" method="getBranchesJSON"> + <param name="address"/> + <result name="*" type="json"> + <param name="branches">branches</param> + </result> + </action> </package> </struts> diff --git a/src/main/webapp/WEB-INF/content/modificationViewer.jsp b/src/main/webapp/WEB-INF/content/modificationViewer.jsp index b30118e..6ae8903 100644 --- a/src/main/webapp/WEB-INF/content/modificationViewer.jsp +++ b/src/main/webapp/WEB-INF/content/modificationViewer.jsp @@ -36,38 +36,17 @@ <!-- Code mirror --> - <!-- First the CodeMirror stuff --> - <script src="codemirror-ui/lib/CodeMirror-2.1/lib/codemirror.js" - type="text/javascript"></script> - <link rel="stylesheet" - href="codemirror-ui/lib/CodeMirror-2.1/lib/codemirror.css"> + <script src="webjars/codemirror/5.1/lib/codemirror.js" type="text/javascript"></script> + <link rel="stylesheet" href="webjars/codemirror/5.1/lib/codemirror.css"> + <script src="webjars/codemirror/5.1/addon/mode/overlay.js" type="text/javascript"></script> - - <script src="codemirror-ui/lib/CodeMirror-2.1/mode/rst/rst.js"></script> - <link rel="stylesheet" - href="codemirror-ui/lib/CodeMirror-2.1/mode/rst/rst.css"> - - <script src="codemirror-ui/lib/CodeMirror-2.1/mode/clike/clike.js"></script> - <link rel="stylesheet" - href="codemirror-ui/lib/CodeMirror-2.1/mode/clike/clike.css"> - - <link rel="stylesheet" - href="codemirror-ui/lib/CodeMirror-2.1/theme/default.css"> - - <script - src="codemirror-ui/lib/CodeMirror-2.1/mode/htmlmixed/htmlmixed.js"></script> - <script - src="codemirror-ui/lib/CodeMirror-2.1/mode/javascript/javascript.js"></script> - <script src="codemirror-ui/lib/CodeMirror-2.1/mode/css/css.js"></script> - <script src="codemirror-ui/lib/CodeMirror-2.1/mode/xml/xml.js"></script> - - <script src="codemirror-ui/lib/CodeMirror-2.1/mode/stex/stex.js"></script> - - <!-- Then the CodeMirrorUI stuff --> - <script src="codemirror-ui/js/codemirror-ui.js" - type="text/javascript"></script> - <link rel="stylesheet" href="codemirror-ui/css/codemirror-ui.css" - type="text/css" media="screen"/> + <script src="webjars/codemirror/5.1/mode/rst/rst.js"></script> + <script src="webjars/codemirror/5.1/mode/clike/clike.js"></script> + <script src="webjars/codemirror/5.1/mode/htmlmixed/htmlmixed.js"></script> + <script src="webjars/codemirror/5.1/mode/javascript/javascript.js"></script> + <script src="webjars/codemirror/5.1/mode/css/css.js"></script> + <script src="webjars/codemirror/5.1/mode/xml/xml.js"></script> + <script src="webjars/codemirror/5.1/mode/stex/stex.js"></script> <script src="js/cancelRedirect.js" type="text/javascript"></script> @@ -339,15 +318,18 @@ </p> -<textarea id="newTextId" rows="50" cols="80" name="newText"><s:property +<textarea id="newTextId" name="newText"><s:property escapeHtml="false" value="OrigText"/></textarea> <script type="text/javascript"> var textarea = document.getElementById('newTextId'); - var uiOptions = { path : 'codemirror-ui/js/', searchMode : 'inline' } - var codeMirrorOptions = { mode: "null" } - var editor = new CodeMirrorUI(textarea, uiOptions, codeMirrorOptions); + var uiOptions = { imagePath : 'codemirror-ui/images/silk', path : 'codemirror-ui/js/', searchMode : 'inline', + buttons : ['undo', 'redo', 'jump', 'reindentSelection', 'reindent'] } + var codeMirrorOptions = { mode: "null", lineNumbers: true } + var editor = CodeMirror.fromTextArea(textarea, { + lineNumbers: true + }); </script> @@ -374,7 +356,7 @@ <script type="text/javascript"> function loadChange() { - document.getElementById('newTextId').value = editor.mirror.getValue(); + document.getElementById('newTextId').value = editor.getValue(); } </script> diff --git a/src/main/webapp/WEB-INF/content/outConnection.jsp b/src/main/webapp/WEB-INF/content/outConnection.jsp index f8eefa2..6ef5d0e 100644 --- a/src/main/webapp/WEB-INF/content/outConnection.jsp +++ b/src/main/webapp/WEB-INF/content/outConnection.jsp @@ -35,6 +35,8 @@ <link rel="icon" href="img/ScmWebEditor_little.png" type="image/png"> <link rel="stylesheet" type="text/css" href="css/main.css"> <title>SCM Web Editor</title> + + <script type="text/javascript" src="js/branches.js"></script> </head> <body> <!-- <a target="_blank" href="http://maven-site.nuiton.org/scmwebeditor/"><img src="img/accueil/machine-a-ecrire.png" alt="$alt" /></a> --> @@ -93,7 +95,9 @@ <p><label><s:text name="scm.outConnection.scmPath"/> <input TYPE=text name="address" - SIZE=50></label> + SIZE=50 id="addressInput"></label> + + <s:hidden id="selectedBranch" name="selectedBranch" value=""/> <s:url id="ajaxSearch" value="search.action"/> @@ -115,6 +119,14 @@ <input type="submit"/> </p> </center> + + <div id="branches"> + <s:text name="scm.outConnection.selectBranch"/> + <select id="branchesList"> + <option value=""><s:text name="scm.outConnection.branches"/> + </select> + </div> + <div id="htmlcontentSearch"></div> </form> @@ -123,7 +135,7 @@ <s:url id="searchTreeUrl" action="search?address=http://localhost/scmsvn"/> - <sjt:tree id="svnTree" + <sjt:tree id="scmTree" jstreetheme="classic" href="%{searchTreeUrl}" onClickTopics="treeClicked"/> diff --git a/src/main/webapp/WEB-INF/content/search.jsp b/src/main/webapp/WEB-INF/content/search.jsp index a40b56a..3686bc1 100644 --- a/src/main/webapp/WEB-INF/content/search.jsp +++ b/src/main/webapp/WEB-INF/content/search.jsp @@ -40,6 +40,21 @@ </script> +<s:if test="address.endsWith('.git')"> + <s:text name="scm.outConnection.headBranch"/> + <s:if test="selectedBranch != null"> + <s:if test="selectedBranch != ''"> + <s:label name="selectedBranch"></s:label> + </s:if> + <s:else> + <s:label name="headBranchName"></s:label> + </s:else> + </s:if> + <s:else> + <s:label name="headBranchName"></s:label> + </s:else> +</s:if> + <div id="searchTree"> <s:set name="address"> <s:property value="address"/> @@ -53,8 +68,8 @@ <s:url id="searchTreeUrl" - action="search?address=%{address}&username=%{username}&pw=%{pw}"/> - <sjt:tree id="svnTree" + action="search?address=%{address}&username=%{username}&pw=%{pw}&selectedBranch=%{selectedBranch}"/> + <sjt:tree id="scmTree" htmlTitles="true" jstreetheme="classic" href="%{searchTreeUrl}" diff --git a/src/main/webapp/WEB-INF/content/uploadForm.jsp b/src/main/webapp/WEB-INF/content/uploadForm.jsp index b4bf0a5..b351a3a 100644 --- a/src/main/webapp/WEB-INF/content/uploadForm.jsp +++ b/src/main/webapp/WEB-INF/content/uploadForm.jsp @@ -70,7 +70,7 @@ <s:url id="searchTreeUrl" action="search?address=%{svnRoot}&username=%{username}&pw=%{pw}"/> - <sjt:tree id="svnTree" + <sjt:tree id="scmTree" htmlTitles="true" jstreetheme="classic" href="%{searchTreeUrl}" diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css index 2d8031e..e28d490 100644 --- a/src/main/webapp/css/main.css +++ b/src/main/webapp/css/main.css @@ -279,11 +279,6 @@ li { text-align:left; } - -.CodeMirror-scroll { - height:500px; -} - #prevtitle { margin-left:13%; } @@ -302,3 +297,16 @@ li { border : solid 1px black; margin-right:15px; } + +.CodeMirror { + width: 80em; + height: 50em; +} + +#branches { + display: none; +} + +.wwgrp { + display: inline; +} diff --git a/src/main/webapp/js/branches.js b/src/main/webapp/js/branches.js new file mode 100644 index 0000000..34f9c4e --- /dev/null +++ b/src/main/webapp/js/branches.js @@ -0,0 +1,36 @@ +var search1; +var search2; + +$(document).ready(function() { + $("#ajaxSearchButton").on("click", function() { + + var listDiv = $("#branches"); + var listSelect = $("#branchesList"); + var address = $("#addressInput").val(); + + if (address.endsWith(".git")) { + + listSelect.find("option:gt(0)").remove(); + + $.getJSON("getBranches.action?address=" + address, function(result) { + $.each(result.branches, function(i, field) { + listSelect.append("<option>" + field + "</option>"); + }); + }); + + listDiv.show(); + } else { + listDiv.hide(); + } + }); + + $("#branchesList").on("change", function() { + + var $searchButton = $("#ajaxSearchButton"); + var $selectedBranch = $("#selectedBranch"); + var selectedBranchName = $("#branchesList").val(); + + $selectedBranch.val(selectedBranchName); + $searchButton.trigger("click"); + }); +}); \ No newline at end of file diff --git a/src/main/webapp/js/selectLanguage.js b/src/main/webapp/js/selectLanguage.js index adb4c4c..78d9073 100644 --- a/src/main/webapp/js/selectLanguage.js +++ b/src/main/webapp/js/selectLanguage.js @@ -37,7 +37,7 @@ function contains(text, test) { function changeModeBy(CodeMirrorEditor, select){ - CodeMirrorEditor.mirror.setOption("mode",select.value); + CodeMirrorEditor.setOption("mode",select.value); } diff --git a/src/test/java/org/nuiton/scmwebeditor/BaseActionTest.java b/src/test/java/org/nuiton/scmwebeditor/BaseActionTest.java index 85a1ed4..03544fb 100644 --- a/src/test/java/org/nuiton/scmwebeditor/BaseActionTest.java +++ b/src/test/java/org/nuiton/scmwebeditor/BaseActionTest.java @@ -120,7 +120,7 @@ public class BaseActionTest { checkoutdirTest = FileUtil.createTempDirectory("scmCheckOutTest_", ""); ScmWebEditorBaseAction baseAction = new ScmWebEditorBaseAction(); - baseAction.checkout(svnSess,checkoutdirTest); + svnSess.checkout(checkoutdirTest); } catch (SVNException e) { log.error("Erreur SVN in test",e); -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.