Mercurial Hosting > nabble
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(); + } + } +} +