diff src/nabble/view/web/catalog/ChangeParent.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children 18cf4872fd7f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nabble/view/web/catalog/ChangeParent.java	Thu Mar 21 19:15:52 2019 -0600
@@ -0,0 +1,284 @@
+
+package nabble.view.web.catalog;
+
+import fschmidt.db.DbDatabase;
+import fschmidt.util.java.HtmlUtils;
+import fschmidt.util.servlet.AuthorizingServlet;
+import nabble.model.Message;
+import nabble.model.ModelException;
+import nabble.model.Node;
+import nabble.model.Person;
+import nabble.model.Site;
+import nabble.model.User;
+import nabble.model.export.Export;
+import nabble.view.lib.Jtp;
+import nabble.view.lib.Permissions;
+import nabble.view.lib.Shared;
+import nabble.view.web.template.UrlMapperNamespace;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+
+public class ChangeParent extends HttpServlet implements AuthorizingServlet {
+	private static final Logger logger = LoggerFactory.getLogger(ChangeParent.class);
+
+	public String getAuthorizationKey(HttpServletRequest request) throws ServletException {
+		return Jtp.getReadAuthorizationKey( Jtp.getSiteNotNull(request).getNode(Jtp.getLong(request,"forum")) );
+	}
+
+	public boolean authorize(String key,HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
+		return Jtp.authorizeForRead(key,request,response);
+	}
+
+	protected void service(HttpServletRequest request, HttpServletResponse response)
+		throws ServletException, IOException
+	{
+		PrintWriter out = response.getWriter();
+		String forumId = request.getParameter("forum");
+		Node forum = forumId == null? null : Jtp.getSiteNotNull(request).getNode(Long.valueOf(forumId));
+
+		if (forum == null)
+			return;
+
+		Person visitor = Jtp.getVisitor(request, response);
+		boolean isSiteAdmin = visitor instanceof User && Permissions.isInGroup((User) visitor, Permissions.ADMINISTRATORS_GROUP);
+
+		boolean allowed = Jtp.canBeRemovedBy(forum,visitor);
+		if (!allowed) {
+			Jtp.login("Only administrators can proceed in this area.", request, response);
+			return;
+		}
+
+		String errorMsg = null;
+		String action = request.getParameter("action");
+		String option = request.getParameter("option");
+		String url = request.getParameter("url");
+		if ("set".equals(action) && "POST".equals(request.getMethod())) {
+			if ("provide-url".equals(option))
+				errorMsg = setParent(forum, url, request, response);
+			else if ("merge".equals(option))
+				errorMsg = merge(forum, visitor, isSiteAdmin, response);
+			else if ("delete-root".equals(option))
+				errorMsg = deleteRoot(forum, visitor, response);
+			else if ("create-parent".equals(option))
+				errorMsg = createParent(forum, (User)visitor, request, response);
+			if (errorMsg == null)
+				return;
+		}
+
+		Node currentParent = forum.getParent();
+		
+		out.print( "\r\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n<html>\r\n	<head>\r\n		" );
+ Shared.title(request, response, "Parent Options"); 
+		out.print( "\r\n		<script type=\"text/javascript\">\r\n			$(document).ready(function() {\r\n				function handleFocus() {\r\n					$('#url,#name,#description,#type').attr('disabled','y');\r\n					var id = $(':radio').filter('[checked]').attr('id');\r\n					if (id == 'choose-parent') {\r\n						$('#url').removeAttr('disabled').focus();\r\n					} else if (id == 'create-parent') {\r\n						$('#name,#description,#type').removeAttr('disabled').focus();\r\n						$('#name').focus();\r\n					}\r\n				};\r\n				handleFocus();\r\n				$(':radio').click(handleFocus);\r\n			});\r\n		</script>\r\n		<style type=\"text/css\">\r\n			div.field-title {\r\n				font-weight:bold;\r\n				font-size:110%;\r\n				padding-bottom:.1em;\r\n				margin-top: 1.7em;\r\n			}\r\n			span.disabled {\r\n				font-size:90%;\r\n				font-weight:normal;\r\n				margin-left:1em\r\n			}\r\n		</style>\r\n	</head>\r\n	<body>\r\n		" );
+ Shared.minHeader(request,response, forum); 
+		out.print( "\r\n		" );
+ Shared.editHeader(forum.getSubjectHtml(), "Parent Options", out); 
+		out.print( "\r\n		" );
+ Shared.errorMessage(request, response, errorMsg, null); 
+		out.print( "\r\n		<form id=\"parent-form\" method=\"post\" action=\"/catalog/ChangeParent.jtp\" accept-charset=\"UTF-8\">\r\n			<input type=\"hidden\" name=\"action\" value=\"set\" />\r\n			<input type=\"hidden\" name=\"forum\" value=\"" );
+		out.print( (forum.getId()) );
+		out.print( "\" />\r\n\r\n			<div class=\"second-font field-title\">\r\n				<input type=\"radio\" id=\"choose-parent\" name=\"option\" value=\"provide-url\" " );
+		out.print( (option == null || "provide-url".equals(option)? "checked='y'" : "") );
+		out.print( "/>\r\n				<label for=\"choose-parent\">Set a New Parent</label>\r\n			</div>\r\n			<div class=\"weak-color\" style=\"margin-left:1.9em\">\r\n				Enter the permalink of the new parent:<br/>\r\n				<input id=\"url\" name=\"url\" size=\"60\" value=\"" );
+		out.print( (Jtp.hideNull(url)) );
+		out.print( "\"/>\r\n			</div>\r\n\r\n			" );
+ boolean isDisabled = !isSiteAdmin || currentParent == null || !Jtp.canBeDeletedBy(forum,visitor); 
+		out.print( "\r\n			<div class=\"second-font field-title\">\r\n				<input type=\"radio\" " );
+		out.print( (isDisabled?"disabled":"") );
+		out.print( " id=\"merge\" name=\"option\" value=\"merge\" " );
+		out.print( ("merge".equals(option)? "checked='y'" : "") );
+		out.print( "/>\r\n				<label for=\"merge\" " );
+		out.print( (isDisabled?"class='weak-color'":"") );
+		out.print( ">Merge into " );
+		out.print( (Jtp.parentName(forum)) );
+		out.print( "</label>\r\n				" );
+		out.print( (isDisabled? "<span class='disabled important'>/ Not Applicable</span>":"") );
+		out.print( "\r\n			</div>\r\n			<div class=\"weak-color\" style=\"margin-left:1.9em\">\r\n				Delete this " );
+		out.print( (Jtp.viewName(forum).toLowerCase()) );
+		out.print( " and move its contents to the " );
+		out.print( (Jtp.parentName(forum).toLowerCase()) );
+		out.print( ". <br/>\r\n			</div>\r\n\r\n			" );
+ isDisabled = !forum.isRoot() || forum.getChildCount() != 1; 
+		out.print( "\r\n			" );
+ Node child = isDisabled? null : forum.getChildren().get(0, 1).get(0); 
+		out.print( "\r\n			" );
+ isDisabled = child != null && child.getKind() == Node.Kind.POST? true : isDisabled; 
+		out.print( "\r\n			<div class=\"second-font field-title\">\r\n				<input type=\"radio\" " );
+		out.print( (isDisabled?"disabled":"") );
+		out.print( " id=\"delete-root\" name=\"option\" value=\"delete-root\" " );
+		out.print( ("delete-root".equals(option)? "checked='y'" : "") );
+		out.print( "/>\r\n				<label for=\"delete-root\" " );
+		out.print( (isDisabled?"class='weak-color'":"") );
+		out.print( ">Make Child the New Root</label>\r\n				" );
+		out.print( (isDisabled? "<span class='disabled important'>/ Not Applicable</span>":"") );
+		out.print( "\r\n			</div>\r\n			<div class=\"weak-color\" style=\"margin-left:1.9em\">\r\n				Delete this " );
+		out.print( (Jtp.viewName(forum).toLowerCase()) );
+		out.print( " and make " );
+		out.print( (child == null? "the single child" : child.getSubjectHtml() ) );
+		out.print( " the new root. <br/>\r\n			</div>\r\n\r\n			" );
+ isDisabled = forum.getParent() != null; 
+		out.print( "\r\n			<div class=\"second-font field-title\">\r\n				<input type=\"radio\" id=\"create-parent\" name=\"option\" value=\"create-parent\" " );
+		out.print( ("create-parent".equals(option)? "checked='y'" : "") );
+		out.print( " " );
+		out.print( (isDisabled?"disabled":"") );
+		out.print( "/>\r\n				<label " );
+		out.print( (isDisabled?"class='weak-color'":"") );
+		out.print( " for=\"create-parent\">Create a New Parent</label>\r\n				" );
+		out.print( (isDisabled? "<span class='disabled important'>/ Not Applicable</span>":"") );
+		out.print( "\r\n			</div>\r\n			<div class=\"weak-color\" style=\"margin-left:1.9em\">\r\n				<table>\r\n					<tr>\r\n						<td>Name:</td>\r\n						<td><input id=\"name\" " );
+		out.print( (isDisabled?"disabled":"") );
+		out.print( " name=\"name\" size=\"30\" value=\"" );
+		out.print( (Jtp.hideNull(request.getParameter("name"))) );
+		out.print( "\" /></td>\r\n					</tr>\r\n					<tr>\r\n						<td>Type:</td>\r\n						<td>\r\n							<select id=\"type\" name=\"type\">\r\n									<option value=\"" );
+		out.print( (Node.Type.FORUM) );
+		out.print( "\">Forum</option>\r\n									<option value=\"" );
+		out.print( (Node.Type.CATEGORY) );
+		out.print( "\">Category</option>\r\n									<option value=\"" );
+		out.print( (Node.Type.BOARD) );
+		out.print( "\">Board</option>\r\n									<option value=\"" );
+		out.print( (Node.Type.MIXED) );
+		out.print( "\">Mixed</option>\r\n									<option value=\"" );
+		out.print( (Node.Type.GALLERY) );
+		out.print( "\">Gallery</option>\r\n									<option value=\"" );
+		out.print( (Node.Type.BLOG) );
+		out.print( "\">Blog</option>\r\n									<option value=\"" );
+		out.print( (Node.Type.NEWS) );
+		out.print( "\">Newspaper</option>\r\n							</select>\r\n						</td>\r\n					</tr>\r\n				</table>\r\n				<div style=\"margin: .5em 0\">\r\n					Description:<br/>\r\n					<textarea id=\"description\" " );
+		out.print( (isDisabled?"disabled":"") );
+		out.print( " name=\"description\" style=\"width:30em;height:10em\">" );
+		out.print( (Jtp.hideNull(request.getParameter("description"))) );
+		out.print( "</textarea>\r\n				</div>\r\n			</div>\r\n\r\n			<div style=\"margin-top:1.4em\">\r\n				<input type=\"submit\" name=\"save\" value=\"Save Changes\" /> or <a href=\"" );
+		out.print( (Jtp.path(forum)) );
+		out.print( "\">Cancel</a>\r\n			</div>\r\n		</form>\r\n\r\n		" );
+ Shared.footer(request, response); 
+		out.print( "\r\n		" );
+ Shared.analytics(request,response); 
+		out.print( "\r\n	</body>\r\n</html>\r\n" );
+
+	}
+
+	private static String setParent(Node app, String url, HttpServletRequest request, HttpServletResponse response)
+		throws IOException, ServletException
+	{
+		if (url == null || url.trim().length() == 0 || !url.startsWith("http://")) {
+			return "You must provide a valid link.";
+		} else {
+			url = Jtp.noCid(url);
+			Node parent = UrlMapperNamespace.getNodeFromUrl(url);
+			if (parent == null || parent.getKind()!=Node.Kind.APP || !parent.getSite().equals(app.getSite())) {
+				if (Export.isValidExportServer(url)) {
+					// Send to export confirmation page
+					response.sendRedirect("/catalog/ExportConfirmation.jtp?node="+app.getId()+"&url="+HtmlUtils.urlEncode(url));
+					return null;
+				} else {
+					return "The link you provided is not a valid Nabble application.";
+				}
+			} else if (parent.getSite().equals(app.getSite()) && parent.getAncestors().contains(app)) {
+				return "Circular relationship is not allowed.";
+			} else {
+				DbDatabase db = app.getSite().getDb();
+				db.beginTransaction();
+				try {
+					Node forumCopy = app.getGoodCopy();
+					forumCopy.changeParent(parent);
+					forumCopy.update();
+					db.commitTransaction();
+
+					forumCopy = forumCopy.getGoodCopy();
+					Shared.javascriptRedirect(request, response, Jtp.url(forumCopy), null, true);
+					return null;
+				} catch(ModelException.NodeLoop e) {
+					if (parent.equals(app))
+						return "The new parent cannot be the forum itself.";
+					else
+						return "The new parent cannot be a descendant of the current forum (circular relationship).";
+				} catch (ModelException e) {
+					logger.error("",e);
+					return "You cannot move this forum here because: "+e.getMessage();
+				} finally {
+					db.endTransaction();
+				}
+			}
+		}
+	}
+
+	private static String merge(Node app, Person visitor, boolean isSiteAdmin, HttpServletResponse response)
+		throws IOException, ServletException
+	{
+		if (isSiteAdmin || Jtp.canBeDeletedBy(app,visitor)) {
+			DbDatabase db = app.getSite().getDb();
+			db.beginTransaction();
+			try {
+				app = app.getGoodCopy();
+				Node parent = app.getParent();
+				for (Node child : app.getChildren()) {
+					child.changeParent(parent);
+				}
+				app.getGoodCopy().deleteMessageOrNode();
+				db.commitTransaction();
+				response.sendRedirect(Jtp.path(parent));
+				return null;
+			} catch (ModelException e) {
+				return e.getMessage();
+			} finally {
+				db.endTransaction();
+			}
+		} else {
+			return "You don't have privileges to delete and merge this sub-forum";
+		}
+	}
+
+	private static String deleteRoot(Node app, Person visitor, HttpServletResponse response)
+		throws IOException, ServletException
+	{
+		if (Jtp.canBeDeletedBy(app,visitor)) {
+			DbDatabase db = app.getSite().getDb();
+			db.beginTransaction();
+			try {
+				Site site = app.getSite();
+				site.deleteRootNode();
+				db.commitTransaction();
+				response.sendRedirect(site.getBaseUrl());
+				return null;
+			} catch (ModelException e) {
+				return e.getMessage();
+			} finally {
+				db.endTransaction();
+			}
+		} else {
+			return "You don't have privileges to make the child a new root";
+		}
+	}
+
+	private static String createParent(Node app, User user, HttpServletRequest request, HttpServletResponse response)
+		throws IOException, ServletException
+	{
+		DbDatabase db = app.getSite().getDb();
+		db.beginTransaction();
+		try {
+			String name = request.getParameter("name");
+			if (name == null || name.trim().length() == 0)
+				return "Please enter a valid name";
+			String description = request.getParameter("description");
+			String type = request.getParameter("type");
+			Node parent = user.newRootNode(Node.Kind.APP, name, description, Message.Format.TEXT, app.getSite(),type);
+			Jtp.addPinnedChild(parent, app.getGoodCopy());
+			db.commitTransaction();
+			response.sendRedirect(Jtp.path(parent));
+			return null;
+		} catch (ModelException e) {
+			return e.getMessage();
+		} finally {
+			db.endTransaction();
+		}
+	}
+}
+