/** * This file is part of OCPsoft SocialPM: Agile Project Management Tools (SocialPM) * * Copyright (c)2011 Lincoln Baxter, III <lincoln@ocpsoft.com> (OCPsoft) * Copyright (c)2011 OCPsoft.com (http://ocpsoft.com) * * If you are developing and distributing open source applications under * the GNU General Public License (GPL), then you are free to re-distribute SocialPM * under the terms of the GPL, as follows: * * SocialPM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SocialPM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with SocialPM. If not, see <http://www.gnu.org/licenses/>. * * For individuals or entities who wish to use SocialPM privately, or * internally, the following terms do not apply: * * For OEMs, ISVs, and VARs who wish to distribute SocialPM with their * products, or host their product online, OCPsoft provides flexible * OEM commercial licenses. * * Optionally, Customers may choose a Commercial License. For additional * details, contact an OCPsoft representative (sales@ocpsoft.com) */ package com.ocpsoft.socialpm.rewrite; import javax.servlet.ServletContext; import com.ocpsoft.common.services.NonEnriching; import com.ocpsoft.rewrite.bind.El; import com.ocpsoft.rewrite.config.Configuration; import com.ocpsoft.rewrite.config.ConfigurationBuilder; import com.ocpsoft.rewrite.config.Direction; import com.ocpsoft.rewrite.config.Not; import com.ocpsoft.rewrite.config.Operation; import com.ocpsoft.rewrite.context.EvaluationContext; import com.ocpsoft.rewrite.event.Rewrite; import com.ocpsoft.rewrite.faces.config.PhaseAction; import com.ocpsoft.rewrite.servlet.config.HttpConfigurationProvider; import com.ocpsoft.rewrite.servlet.config.Path; import com.ocpsoft.rewrite.servlet.config.Redirect; import com.ocpsoft.rewrite.servlet.config.rule.Join; /** * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ public class ProjectRewriteConfiguration extends HttpConfigurationProvider implements NonEnriching { private static final String PROJECT = "[a-zA-Z0-9-]+"; @Override public Configuration getConfiguration(final ServletContext context) { return ConfigurationBuilder .begin() // Canonicalize project name TODO support outbound .defineRule() .when(Direction.isInbound().andNot(Path.matches(".*xhtml")).and( Path.matches("/{profile}/{project}") .where("profile") .constrainedBy(new RegexConstraint("(?=.*[A-Z]+.*).*")) .transformedBy(new ToLowerCase()) // TODO this creates an implicit AND operator... probably need an alternative .where("project") .constrainedBy(new RegexConstraint("(?=.*[A-Z]+.*).*")) .transformedBy(new ToLowerCase()))) .perform(Redirect.permanent(context.getContextPath() + "/{profile}/{project}")) /* * Transform Profile Name and Project Slug */ .defineRule() .when(Direction.isInbound().andNot(Path.matches(".*xhtml")).and( Path.matches("/{profile}/{project}{tail}") .where("profile") .constrainedBy(new RegexConstraint("(?=.*[A-Z]+.*).*")) .transformedBy(new ToLowerCase()) .where("project") .constrainedBy(new RegexConstraint("(?=.*[A-Z]+.*).*")) .transformedBy(new ToLowerCase()) .where("tail").matches("/.*"))) .perform(Redirect.permanent(context.getContextPath() + "/{profile}/{project}{tail}")) /* * Bind profile and project value to EL & Load current Project */ .defineRule() .when(Direction.isInbound() .andNot(Path.matches(".*xhtml")) .and(Path.matches("/{profile}.*").where("profile") .bindsTo(El.property("params.profileUsername")))) .perform(new Operation() { @Override public void perform(final Rewrite event, final EvaluationContext context) {} }) /** * <p/> * TODO we need a better way to figure out if a profile does not exist, then to show something else * perhaps a database validation or constraint? on the above profile property injection? */ .addRule(Join.path("/{profile}").to("/pages/profile.xhtml") .when(Not.any(Resource.exists("/pages/{profile}.xhtml"))) .perform(PhaseAction.retrieveFrom(El.retrievalMethod("profiles.verifyExists")))) .defineRule() .when( Direction.isInbound() .andNot(Path.matches(".*xhtml")) .and(Direction.isInbound()) .and(Path.matches("/{profile}/{project}.*") .where("profile").matches(PROJECT) .where("project").matches(PROJECT) .bindsTo(El.property("params.projectSlug"))) ) .perform(PhaseAction.retrieveFrom(El.retrievalMethod("projects.loadCurrent"))) /* * Project Pages */ .addRule(Join.path("/{profile}/{project}") .to("/pages/project/view.xhtml") .where("project").matches(PROJECT) .when(SocialPMResources.excluded()).perform(null) ) .addRule(Join.path("/{profile}/{project}/issues") .to("/pages/project/backlog.xhtml") .where("project").matches(PROJECT) .when(SocialPMResources.excluded()) ) /* * Story pages */ .addRule(Join.path("/{profile}/{project}/stories/new") .to("/pages/story/create.xhtml") .where("project").matches(PROJECT) .when(SocialPMResources.excluded()) ) /* * Bind story number to EL and Load current story */ .defineRule() .when( Direction.isInbound() .andNot(Path.matches(".*xhtml")) .and(Direction.isInbound()) .and(Path.matches("/{profile}/{project}/story/{story}.*") .where("profile").matches(PROJECT) .where("project").matches(PROJECT) .where("story").matches(PROJECT) .bindsTo(El.property("params.storyNumber"))) ) .perform(PhaseAction.retrieveFrom(El.retrievalMethod("stories.loadCurrent"))) .addRule(Join.path("/{profile}/{project}/story/{story}") .to("/pages/story/view.xhtml") .where("project").matches(PROJECT) .when(SocialPMResources.excluded()) ) /* * Iteration pages */ /* * Bind iteration number to EL and Load current story */ .defineRule() .when( Direction.isInbound() .andNot(Path.matches(".*xhtml")) .and(Direction.isInbound()) .and(Path.matches("/{profile}/{project}/iteration/{iteration}.*") .where("profile").matches(PROJECT) .where("project").matches(PROJECT) .where("iteration").matches(PROJECT) .constrainedBy(new IntegerConstraint(1, null)) .bindsTo(El.property("params.iterationNumber"))) ) .perform(PhaseAction.retrieveFrom(El.retrievalMethod("iterations.loadCurrent"))) .addRule(Join.path("/{profile}/{project}/iteration/{iteration}") .to("/pages/iteration/sorter.xhtml") .where("project").matches(PROJECT) .where("iteration").matches("\\d+") .constrainedBy(new IntegerConstraint(1, null)) .when(SocialPMResources.excluded()).withId("iteration-view") ) ; } @Override public int priority() { return 20; } }