001: package org.tigris.scarab.actions;
002:
003: /* ================================================================
004: * Copyright (c) 2000-2002 CollabNet. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are
008: * met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowlegement: "This product includes
019: * software developed by Collab.Net <http://www.Collab.Net/>."
020: * Alternately, this acknowlegement may appear in the software itself, if
021: * and wherever such third-party acknowlegements normally appear.
022: *
023: * 4. The hosted project names must not be used to endorse or promote
024: * products derived from this software without prior written
025: * permission. For written permission, please contact info@collab.net.
026: *
027: * 5. Products derived from this software may not use the "Tigris" or
028: * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
029: * prior written permission of Collab.Net.
030: *
031: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
032: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
033: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
034: * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
035: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
036: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
037: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
038: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
039: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
040: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
041: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042: *
043: * ====================================================================
044: *
045: * This software consists of voluntary contributions made by many
046: * individuals on behalf of Collab.Net.
047: */
048:
049: import java.util.ArrayList;
050: import java.util.HashSet;
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.Set;
054:
055: import org.apache.fulcrum.intake.model.Group;
056: import org.apache.fulcrum.parser.ParameterParser;
057: import org.apache.turbine.RunData;
058: import org.apache.turbine.TemplateContext;
059: import org.apache.turbine.Turbine;
060: import org.apache.turbine.tool.IntakeTool;
061: import org.tigris.scarab.actions.base.BaseModifyIssue;
062: import org.tigris.scarab.notification.NotificationManager;
063: import org.tigris.scarab.notification.NotificationManagerFactory;
064: import org.tigris.scarab.om.ActivitySet;
065: import org.tigris.scarab.notification.ActivityType;
066: import org.tigris.scarab.om.AttributePeer;
067: import org.tigris.scarab.om.AttributeValuePeer;
068: import org.tigris.scarab.om.Issue;
069: import org.tigris.scarab.om.IssueType;
070: import org.tigris.scarab.om.IssueTypeManager;
071: import org.tigris.scarab.om.Module;
072: import org.tigris.scarab.om.ModuleManager;
073: import org.tigris.scarab.om.ScarabUser;
074: import org.tigris.scarab.services.security.ScarabSecurity;
075: import org.tigris.scarab.tools.ScarabLocalizationTool;
076: import org.tigris.scarab.tools.ScarabRequestTool;
077: import org.tigris.scarab.tools.localization.L10NKeySet;
078: import org.tigris.scarab.tools.localization.L10NMessage;
079: import org.tigris.scarab.util.Email;
080: import org.tigris.scarab.util.EmailContext;
081: import org.tigris.scarab.util.Log;
082: import org.tigris.scarab.util.ScarabConstants;
083:
084: /**
085: * This class is responsible for moving/copying an issue
086: * from one module to another.
087: *
088: * @author <a href="mailto:elicia@collab.net">Elicia David</a>
089: * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
090: * @version $Id: MoveIssue.java 10191 2006-06-26 23:02:22Z dabbous $
091: */
092: public class MoveIssue extends BaseModifyIssue {
093:
094: /**
095: * From MoveIssue.vm -> MoveIssue2.vm, we only need to validate the inputs.
096: * Intake + Pull is so friggen cool.
097: */
098: public void doValidate(RunData data, TemplateContext context)
099: throws Exception {
100: boolean collisionOccurred = isCollision(data, context);
101: context.put("collisionDetectedOnMoveAttempt",
102: collisionOccurred ? Boolean.TRUE : Boolean.FALSE);
103: if (collisionOccurred) {
104: // Report the collision to the user.
105: doCancel(data, context);
106: return;
107: }
108:
109: IntakeTool intake = getIntakeTool(context);
110: if (!intake.isAllValid()) {
111: return;
112: }
113:
114: ScarabLocalizationTool l10n = getLocalizationTool(context);
115: ScarabRequestTool scarabR = getScarabRequestTool(context);
116: String[] issueIds = data.getParameters()
117: .getStrings("issue_ids");
118: List issues = new ArrayList();
119: Issue issue = null;
120: if (issueIds == null || issueIds.length == 0) {
121: scarabR.setAlertMessage(L10NKeySet.SelectIssueToMove);
122: return;
123: } else {
124: for (int i = 0; i < issueIds.length; i++) {
125: issues.add(scarabR.getIssue(issueIds[i]));
126: }
127: issue = (Issue) issues.get(0);
128: }
129:
130: Module oldModule = issue.getModule();
131: Group moveIssue = intake.get("MoveIssue",
132: IntakeTool.DEFAULT_KEY, false);
133: String[] modIssueTypes = data.getParameters().getStrings(
134: "mod_issuetype");
135: String modIssueType = null;
136: if (modIssueTypes != null) {
137: for (int i = 0; i < modIssueTypes.length; i++) {
138: String testOption = modIssueTypes[i];
139: if (testOption != null && testOption.length() > 0) {
140: if (modIssueType == null) {
141: modIssueType = testOption;
142: } else {
143: scarabR
144: .setAlertMessage(L10NKeySet.OnlySelectOneDestination);
145: return;
146: }
147: }
148: }
149:
150: }
151:
152: if (modIssueType == null) {
153: scarabR
154: .setAlertMessage(L10NKeySet.SelectModuleAndIssueType);
155: return;
156: }
157:
158: Integer newModuleId = null;
159: Integer newIssueTypeId = null;
160: Module newModule = null;
161: try {
162: newModuleId = new Integer(modIssueType.substring(0,
163: modIssueType.indexOf('_')));
164: newIssueTypeId = new Integer(modIssueType.substring(
165: modIssueType.indexOf('_') + 1, modIssueType
166: .length()));
167: newModule = ModuleManager.getInstance(newModuleId);
168:
169: } catch (Exception e) {
170: scarabR
171: .setAlertMessage(L10NKeySet.SelectModuleAndIssueType);
172: return;
173: }
174:
175: String selectAction = moveIssue.get("Action").toString();
176: ScarabUser user = (ScarabUser) data.getUser();
177: boolean changeModule = !newModuleId.equals(oldModule
178: .getModuleId());
179: boolean changeIssueType = !newIssueTypeId.equals(issue
180: .getIssueType().getIssueTypeId());
181:
182: // Check permissions
183: // Must have ISSUE_ENTER in new module
184: // If moving to a new module, must have ISSUE_MOVE in old module
185: // If moving to a new issue type, must have ISSUE_EDIT in old module
186: if (!user.hasPermission(ScarabSecurity.ISSUE__ENTER, newModule)) {
187: data.setMessage(l10n.get(NO_PERMISSION_MESSAGE));
188: return;
189: }
190: if ("move".equals(selectAction)) {
191: if (changeModule
192: && !user.hasPermission(ScarabSecurity.ISSUE__MOVE,
193: oldModule)
194: || (changeIssueType && !user.hasPermission(
195: ScarabSecurity.ISSUE__EDIT, oldModule))) {
196: data.setMessage(l10n.get(NO_PERMISSION_MESSAGE));
197: return;
198: }
199: }
200: // Do not allow user to move issue if source and destination
201: // Module and issue type are the same
202: if ("move".equals(selectAction) && !changeModule
203: && !changeIssueType) {
204: scarabR.setAlertMessage(L10NKeySet.CannotMoveToSameModule);
205: return;
206: }
207:
208: context.put("newModuleId", newModuleId.toString());
209: context.put("newIssueTypeId", newIssueTypeId.toString());
210: String nextTemplate = getNextTemplate(data);
211: setTarget(data, nextTemplate);
212: }
213:
214: /**
215: * Deals with moving or copying an issue from one module to
216: * another module.
217: */
218: public void doSaveissue(RunData data, TemplateContext context)
219: throws Exception {
220: boolean collisionOccurred = isCollision(data, context);
221: context.put("collisionDetectedOnMoveAttempt",
222: collisionOccurred ? Boolean.TRUE : Boolean.FALSE);
223: if (collisionOccurred) {
224: // Report the collision to the user.
225: setTarget(data, "ViewIssue.vm");
226: return;
227: }
228:
229: IntakeTool intake = getIntakeTool(context);
230: ScarabRequestTool scarabR = getScarabRequestTool(context);
231: if (!intake.isAllValid()) {
232: return;
233: }
234:
235: ScarabLocalizationTool l10n = getLocalizationTool(context);
236: String[] issueIds = data.getParameters()
237: .getStrings("issue_ids");
238: List issues = new ArrayList();
239: Issue issue = null;
240: if (issueIds == null || issueIds.length == 0) {
241: scarabR.setAlertMessage(L10NKeySet.SelectIssueToMove);
242: return;
243: } else {
244: for (int i = 0; i < issueIds.length; i++) {
245: issues.add(scarabR.getIssue(issueIds[i]));
246: }
247: issue = (Issue) issues.get(0);
248: }
249:
250: Module oldModule = issue.getModule();
251: IssueType oldIssueType = issue.getIssueType();
252: Group moveIssue = intake.get("MoveIssue",
253: IntakeTool.DEFAULT_KEY, false);
254: Integer newModuleId = ((Integer) moveIssue.get("ModuleId")
255: .getValue());
256: Integer newIssueTypeId = ((Integer) moveIssue
257: .get("IssueTypeId").getValue());
258: Module newModule = ModuleManager.getInstance(newModuleId);
259: IssueType newIssueType = IssueTypeManager
260: .getInstance(newIssueTypeId);
261: String selectAction = moveIssue.get("Action").toString();
262: ScarabUser user = (ScarabUser) data.getUser();
263:
264: ActivityType activityType = null;
265: if (selectAction.equals("move"))
266: activityType = ActivityType.ISSUE_MOVED;
267: else
268: activityType = ActivityType.ISSUE_COPIED;
269:
270: // Get selected non-matching attributes to save in comment
271: List commentAttrs = new ArrayList();
272: List commentUserValues = new ArrayList();
273: ParameterParser params = data.getParameters();
274: Object[] keys = params.getKeys();
275: for (int i = 0; i < keys.length; i++) {
276: String key = (String) keys[i];
277: if (key.startsWith("comment_attr_ids_")) {
278: commentAttrs.add(scarabR.getAttribute(new Integer(key
279: .substring(17))));
280: }
281: if (key.startsWith("comment_user_attval_")) {
282: Long valueId = new Long(key.substring(20));
283: commentUserValues.add(AttributeValuePeer
284: .retrieveByPK(valueId));
285: }
286: }
287: String reason = params.getString("reason");
288: if (reason == null || reason.trim().length() == 0) {
289: scarabR.setAlertMessage(L10NKeySet.ReasonRequired);
290: return;
291: }
292:
293: Issue newIssue = null;
294: for (int i = 0; i < issues.size(); i++) {
295: issue = (Issue) issues.get(i);
296: // Do the copy/move
297: try {
298: newIssue = issue.move(newModule, newIssueType,
299: selectAction, user, reason, commentAttrs,
300: commentUserValues);
301: } catch (Exception e) {
302: L10NMessage l10nMessage = new L10NMessage(
303: L10NKeySet.ErrorExceptionMessage, e);
304: scarabR.setAlertMessage(l10nMessage);
305: Log.get().warn("Exception during issue copy/move", e);
306: return;
307: }
308:
309: // Send notification for the target issue/module
310: NotificationManagerFactory.getInstance()
311: .addActivityNotification(activityType,
312: newIssue.getLastActivitySet(), issue, null,
313: null, user);
314:
315: // If it's moved, should also notify users associated in the old
316: // location because the issue is dissapearing from there.
317: if (activityType == ActivityType.ISSUE_MOVED) {
318: NotificationManagerFactory.getInstance()
319: .addActivityNotification(activityType,
320: issue.getLastActivitySet(), issue,
321: null, null, user);
322: }
323: }
324:
325: // Redirect to moved or copied issue
326: if (issues.size() == 1) {
327: data.getParameters().remove("id");
328: data.getParameters().add("id",
329: newIssue.getUniqueId().toString());
330: setTarget(data, "ViewIssue.vm");
331: } else {
332: setTarget(data, "IssueList.vm");
333: }
334:
335: scarabR.setConfirmMessage(DEFAULT_MSG);
336: }
337:
338: /**
339: * This manages clicking the Back button on MoveIssue2.vm
340: */
341: public void doBacktoone(RunData data, TemplateContext context)
342: throws Exception {
343: setTarget(data, data.getParameters().getString(
344: ScarabConstants.CANCEL_TEMPLATE, "MoveIssue.vm"));
345: }
346: }
|