public static void pageArticles_Images_Upload(ref StringBuilder content, string pluginid, Connector conn, ref Misc.PageElements pageElements, HttpRequest request, HttpResponse response, bool permCreate) { // Upload an image // -- Ensure the user has creation permissions, else we'll 404 if (!permCreate) return; string error = null; HttpPostedFile image = request.Files["image"]; string title = request.Form["title"]; string captcha = request.Form["captcha"]; // Check for postback if (title != null && captcha != null && image != null) { // Validate if (!Common.Validation.validCaptcha(captcha)) error = "Incorrect captcha verification code, please try again!"; else if (title.Length < Core.settings[SETTINGS_KEY].getInt(SETTINGS_IMAGES_TITLE_MIN) || title.Length > Core.settings[SETTINGS_KEY].getInt(SETTINGS_TITLE_MAX)) error = "Title must be between " + Core.settings[SETTINGS_KEY][SETTINGS_TITLE_MIN] + " to " + Core.settings[SETTINGS_KEY][SETTINGS_IMAGES_TITLE_MAX] + " characters in length!"; else if (image.ContentLength == 0) error = "The uploaded image contains no data, please try again!"; else if (image.ContentLength > Core.settings[SETTINGS_KEY].getInt(SETTINGS_IMAGES_MAXSIZE)) error = "The uploaded image is too large - maximum size allowed is " + Misc.Plugins.getBytesString(Core.settings[SETTINGS_KEY].getLong(SETTINGS_IMAGES_MAXSIZE)) + "!"; else if (!Core.settings[SETTINGS_KEY].getCommaArrayContains(SETTINGS_IMAGE_TYPES, image.ContentType)) error = "Invalid image type - ensure you've uploaded an actual image!"; else { // Compress the image data for database storage byte[] imageData = compressImageData(image.InputStream, Core.settings[SETTINGS_KEY].getInt(SETTINGS_IMAGES_MAXWIDTH), Core.settings[SETTINGS_KEY].getInt(SETTINGS_IMAGES_MAXHEIGHT)); if (imageData == null) error = "Failed to process image - please try your request again or ensure the uploaded image is not corrupt!"; else { // Write the data to the database Dictionary<string, object> imageParams = new Dictionary<string, object>(); imageParams.Add("title", title); imageParams.Add("userid", HttpContext.Current.User.Identity.Name); imageParams.Add("data", imageData); string imageid = conn.Query_Scalar_Parameters("INSERT INTO articles_images (title, userid, data, datetime) VALUES(@title, @userid, @data, NOW()); SELECT LAST_INSERT_ID();", imageParams).ToString(); // Redirect the user to view the image conn.Disconnect(); response.Redirect(pageElements["URL"] + "/articles/images/view/" + imageid); } } } // Output form content.Append( Core.templates["articles"]["image_uploader"] .Replace("<ERROR>", error != null ? Core.templates[pageElements["TEMPLATE"]]["error"].Replace("<ERROR>", error) : string.Empty) .Replace("<TITLE>", HttpUtility.HtmlEncode(title)) ); pageElements["TITLE"] = "Articles - Image Store - Upload"; }
/// <summary> /// Used to create/modify an article. /// </summary> /// <param name="pluginid"></param> /// <param name="conn"></param> /// <param name="pageElements"></param> /// <param name="request"></param> /// <param name="response"></param> public static void pageArticle_Editor(string pluginid, Connector conn, ref Misc.PageElements pageElements, HttpRequest request, HttpResponse response) { // Check the user is logged-in, else redirect to the login page if (!HttpContext.Current.User.Identity.IsAuthenticated) response.Redirect(pageElements["URL"] + "/login", true); // Load the users permissions and check they're able to create articles Result perms = conn.Query_Read("SELECT ug.access_media_create, ug.access_media_publish, ug.access_media_edit, ug.access_admin FROM bsa_users AS u LEFT OUTER JOIN bsa_user_groups AS ug ON ug.groupid=u.groupid WHERE u.userid='" + Utils.Escape(HttpContext.Current.User.Identity.Name) + "'"); if (perms.Rows.Count != 1 || !perms[0]["access_media_create"].Equals("1")) return; bool permAdmin = perms[0]["access_admin"].Equals("1"); bool permEdit = perms[0]["access_media_edit"].Equals("1"); bool permPublish = perms[0]["access_media_publish"].Equals("1"); string error = null; Result preData = null; ResultRow preDataRow = null; // Check if we're modifying an existing article, if so we'll load the data string articleid = request.QueryString["articleid"]; if (articleid != null && Misc.Plugins.isNumeric(articleid)) { // Attempt to load the pre-existing article's data preData = conn.Query_Read("SELECT a.*, at.relative_url, at.pdf_name, GROUP_CONCAT(at2.keyword SEPARATOR ',') AS tags FROM articles AS a LEFT OUTER JOIN articles_tags AS at2 ON (EXISTS (SELECT tagid FROM articles_tags_article WHERE tagid=at2.tagid AND articleid='" + Utils.Escape(articleid) + "')) LEFT OUTER JOIN articles_thread AS at ON at.threadid=a.threadid WHERE articleid='" + Utils.Escape(articleid) + "'"); if (preData.Rows.Count != 1) preData = null; else preDataRow = preData[0]; } // Check for postback string title = request.Form["title"]; string body = request.Form["body"]; string relativeUrl = request.Form["relative_url"] ?? request.QueryString["relative_url"]; string tags = request.Form["tags"]; bool allowHTML = request.Form["allow_html"] != null; bool allowComments = request.Form["allow_comments"] != null; bool showPane = request.Form["show_pane"] != null; bool inheritThumbnail = request.Form["inherit_thumbnail"] != null; bool updateExisting = request.Form["update_existing"] != null; HttpPostedFile thumbnail = request.Files["thumbnail"]; if (title != null && body != null && relativeUrl != null && tags != null) { // Validate if (title.Length < Core.settings[SETTINGS_KEY].getInt(SETTINGS_TITLE_MIN) || title.Length > Core.settings[SETTINGS_KEY].getInt(SETTINGS_TITLE_MAX)) error = "Title must be " + Core.settings[SETTINGS_KEY][SETTINGS_TITLE_MIN] + " to " + Core.settings[SETTINGS_KEY][SETTINGS_TITLE_MAX] + " characters in length!"; else if (body.Length < Core.settings[SETTINGS_KEY].getInt(SETTINGS_BODY_MIN) || body.Length > Core.settings[SETTINGS_KEY].getInt(SETTINGS_BODY_MAX)) error = "Body must be " + Core.settings[SETTINGS_KEY][SETTINGS_BODY_MIN] + " to " + Core.settings[SETTINGS_KEY][SETTINGS_BODY_MAX] + " characters in length!"; else if (body.Replace(" ", string.Empty).Length == 0) error = "Body cannot be empty/contain just spaces!"; else if (thumbnail != null && thumbnail.ContentLength > 0 && thumbnail.ContentLength > Core.settings[SETTINGS_KEY].getInt(SETTINGS_THUMBNAIL_MAXSIZE)) error = "Thumbnail cannot exceed " + Core.settings[SETTINGS_KEY][SETTINGS_THUMBNAIL_MAXSIZE] + " bytes (" + Misc.Plugins.getBytesString(Core.settings[SETTINGS_KEY].getInt(SETTINGS_THUMBNAIL_MAXSIZE)) + ")!"; else if (thumbnail != null && thumbnail.ContentLength > 0 && !Core.settings[SETTINGS_KEY].getCommaArrayContains(SETTINGS_IMAGE_TYPES, thumbnail.ContentType)) error = "Invalid thumbnail image format - ensure you uploaded an image!"; else if ((error = validRelativeUrl(relativeUrl, Core.settings[SETTINGS_KEY].getInt(SETTINGS_RELATIVE_URL_MAXCHUNKS), Core.settings[SETTINGS_KEY].getInt(SETTINGS_RELATIVE_URL_CHUNK_MIN), Core.settings[SETTINGS_KEY].getInt(SETTINGS_RELATIVE_URL_CHUNK_MAX))) != null) ; else { // Verify the user has not exceeded post limits for today - unless they're admin, we'll just skip the checks ResultRow postLimits = permAdmin ? null : conn.Query_Read("SELECT (SELECT COUNT('') FROM articles WHERE userid='" + Utils.Escape(HttpContext.Current.User.Identity.Name) + "' AND datetime >= DATE_SUB(NOW(), INTERVAL 1 HOUR)) AS articles_hour, (SELECT COUNT('') FROM articles WHERE userid='" + Utils.Escape(HttpContext.Current.User.Identity.Name) + "' AND datetime >= DATE_SUB(NOW(), INTERVAL 1 DAY)) AS articles_day")[0]; if (postLimits != null && int.Parse(postLimits["articles_hour"]) >= Core.settings[SETTINGS_KEY].getInt(SETTINGS_ARTICLES_EDIT_PER_HOUR)) error = "You've already posted the maximum amount of articles allowed within an hour, please try again later!"; else if (postLimits != null && int.Parse(postLimits["articles_day"]) >= Core.settings[SETTINGS_KEY].getInt(SETTINGS_ARTICLES_EDIT_PER_DAY)) error = "You've already posted the maximum amount of articles allowed today, please try again later!"; else { // Verify tags ArticleTags parsedTags = getTags(tags, Core.settings[SETTINGS_KEY].getInt(SETTINGS_TAGS_TITLE_MIN), Core.settings[SETTINGS_KEY].getInt(SETTINGS_TAGS_TITLE_MAX), Core.settings[SETTINGS_KEY].getInt(SETTINGS_TAGS_MAX)); if (parsedTags.error != null) error = parsedTags.error; else { // Check if we're inserting, else perhaps inheriting, a thumbnail string thumbnailid = null; if (thumbnail != null && thumbnail.ContentLength > 0) { byte[] imageData = compressImageData(thumbnail.InputStream, Core.settings[SETTINGS_KEY].getInt(SETTINGS_THUMBNAIL_MAXWIDTH), Core.settings[SETTINGS_KEY].getInt(SETTINGS_THUMBNAIL_MAXHEIGHT)); if (imageData != null) { // Success - insert thumbnail and get thumbnailid Dictionary<string, object> thumbParams = new Dictionary<string, object>(); thumbParams.Add("thumb", imageData); thumbnailid = conn.Query_Scalar_Parameters("INSERT INTO articles_thumbnails (data) VALUES(@thumb); SELECT LAST_INSERT_ID();", thumbParams).ToString(); } else error = "Failed to process thumbnail image, please try again or report this to the site administrator!"; } else if (inheritThumbnail && preDataRow != null && preDataRow["thumbnailid"].Length != 0) { // Grab pre-existing thumbnailid thumbnailid = preDataRow["thumbnailid"]; } // Ensure no thumbnail processing errors occur, else do not continue if (error == null) { // Format the body formatting for caching StringBuilder cached = new StringBuilder(body); articleViewRebuildCache(conn, ref cached, allowHTML, ref pageElements); // Posted data is valid, check if the thread exists - else create it bool updateArticle = false; // If the article is being modified and it has not been published and it's owned by the same user -> update it (user may make a small change) string threadid; Result threadCheck = conn.Query_Read("SELECT threadid FROM articles_thread WHERE relative_url='" + Utils.Escape(relativeUrl) + "'"); if (threadCheck.Rows.Count == 1) { // -- Thread exists threadid = threadCheck[0]["threadid"]; // -- Check if to update the article if the articleid has been specified if (articleid != null) { Result updateCheck = conn.Query_Read("SELECT userid, published FROM articles WHERE articleid='" + Utils.Escape(articleid) + "' AND threadid='" + Utils.Escape(threadid) + "'"); // If the article is unpublished *or* update-existing is true, set the update flag to true bool ucPublished = updateCheck[0]["published"].Equals("1"); if (updateCheck.Rows.Count == 1 && ((updateCheck[0]["userid"] == HttpContext.Current.User.Identity.Name && !ucPublished) || (updateExisting && (permAdmin || permEdit)))) updateArticle = true; } } else // -- Create thread threadid = conn.Query_Scalar("INSERT INTO articles_thread (relative_url) VALUES('" + Utils.Escape(relativeUrl) + "'); SELECT LAST_INSERT_ID();").ToString(); // Check if to insert or update the article if (updateArticle) { StringBuilder query = new StringBuilder(); // Update the article query .Append("UPDATE articles SET title='").Append(Utils.Escape(title)) .Append("', thumbnailid=").Append(thumbnailid != null ? "'" + Utils.Escape(thumbnailid) + "'" : "NULL") .Append(", body='").Append(Utils.Escape(body)) .Append("', body_cached='").Append(Utils.Escape(cached.ToString())) .Append("', allow_comments='").Append(allowComments ? "1" : "0") .Append("', allow_html='").Append(allowHTML ? "1" : "0") .Append("', show_pane='").Append(showPane ? "1" : "0").Append("' WHERE articleid='").Append(Utils.Escape(articleid)).Append("';"); // Delete the previous tags query.Append("DELETE FROM articles_tags_article WHERE articleid='" + Utils.Escape(articleid) + "';"); // Delete the previous images associated with the article query.Append("DELETE FROM articles_images_links WHERE articleid='" + Utils.Escape(articleid) + "'"); // -- Execute query conn.Query_Execute(query.ToString()); } else { // Insert article and link to the thread StringBuilder query = new StringBuilder(); query .Append("INSERT INTO articles (threadid, title, userid, body, body_cached, moderator_userid, published, allow_comments, allow_html, show_pane, thumbnailid, datetime) VALUES('") .Append(Utils.Escape(threadid)) .Append("', '").Append(Utils.Escape(title)) .Append("', '").Append(Utils.Escape(HttpContext.Current.User.Identity.Name)) .Append("', '").Append(Utils.Escape(body)) .Append("', '").Append(Utils.Escape(cached.ToString())) .Append("', ").Append(permPublish ? "'" + Utils.Escape(HttpContext.Current.User.Identity.Name) + "'" : "NULL") .Append(", '").Append(permPublish ? "1" : "0") .Append("', '").Append(allowComments ? "1" : "0") .Append("', '").Append(allowHTML ? "1" : "0") .Append("', '").Append(showPane ? "1" : "0") .Append("', ").Append(thumbnailid != null ? "'" + Utils.Escape(thumbnailid) + "'" : "NULL") .Append(", NOW()); SELECT LAST_INSERT_ID();"); articleid = conn.Query_Scalar(query.ToString()).ToString(); // If this was automatically published, set it as the current article for the thread if (permPublish) conn.Query_Execute("UPDATE articles_thread SET articleid_current='" + Utils.Escape(articleid) + "' WHERE relative_url='" + Utils.Escape(relativeUrl) + "'"); } // Add/update pdf pdfRebuild(pluginid, articleid, title, preData != null ? preDataRow["pdf_name"] : string.Empty, threadid, request); // Add the new tags and delete any tags not used by any other articles, as well as cleanup unused thumbnails StringBuilder finalQuery = new StringBuilder(); if (parsedTags.tags.Count > 0) { StringBuilder tagsInsertQuery = new StringBuilder(); StringBuilder tagsArticleQuery = new StringBuilder(); foreach (string tag in parsedTags.tags) { // -- Attempt to insert the tags - if they exist, they wont be inserted tagsInsertQuery.Append("('" + Utils.Escape(tag) + "'),"); tagsArticleQuery.Append("((SELECT tagid FROM articles_tags WHERE keyword='" + Utils.Escape(tag) + "'), '" + Utils.Escape(articleid) + "'),"); } // -- Build final query finalQuery.Append("INSERT IGNORE INTO articles_tags (keyword) VALUES") .Append(tagsInsertQuery.Remove(tagsInsertQuery.Length - 1, 1).ToString()) .Append("; INSERT IGNORE INTO articles_tags_article (tagid, articleid) VALUES") .Append(tagsArticleQuery.Remove(tagsArticleQuery.Length - 1, 1).ToString()) .Append(";"); } // Add any linked imagery // -- Find the unique valid image IDs List<string> images = new List<string>(); foreach (Match m in Regex.Matches(body, REGEX_IMAGE_STORE, RegexOptions.Multiline)) if (!images.Contains(m.Groups[1].Value)) images.Add(m.Groups[1].Value); foreach (Match m in Regex.Matches(body, REGEX_IMAGE_STORE_CUSTOM_W, RegexOptions.Multiline)) if (!images.Contains(m.Groups[3].Value)) images.Add(m.Groups[3].Value); foreach (Match m in Regex.Matches(body, REGEX_IMAGE_STORE_CUSTOM_WH, RegexOptions.Multiline)) if (!images.Contains(m.Groups[3].Value)) images.Add(m.Groups[3].Value); if (images.Count != 0) { // -- Insert all the valid IDs which exist in the actual articles_images table finalQuery.Append("INSERT IGNORE INTO articles_images_links (articleid, imageid) SELECT '" + Utils.Escape(articleid) + "' AS articleid, imageid FROM articles_images WHERE imageid IN ("); foreach (string s in images) finalQuery.Append("'").Append(Utils.Escape(s)).Append("',"); finalQuery.Remove(finalQuery.Length - 1, 1).Append(");"); } // -- This will delete any tags in the main table no longer used in the articles tags table finalQuery.Append(QUERY_TAGS_CLEANUP); // -- This will delete any unused thumbnail images finalQuery.Append(QUERY_THUMBNAIL_CLEANUP); // -- This will log the event finalQuery.Append(insertEvent(updateArticle ? RecentChanges_EventType.Edited : RecentChanges_EventType.Created, HttpContext.Current.User.Identity.Name, articleid, threadid)); // -- Execute final query conn.Query_Execute(finalQuery.ToString()); // Redirect to the new article conn.Disconnect(); response.Redirect(pageElements["URL"] + "/article/" + articleid, true); } } } } } // Display form pageElements["CONTENT"] = Core.templates["articles"]["editor"] .Replace("<ERROR>", error != null ? Core.templates[pageElements["TEMPLATE"]]["error"].Replace("<ERROR>", HttpUtility.HtmlEncode(error)) : string.Empty) .Replace("<PARAMS>", preData != null ? "articleid=" + HttpUtility.UrlEncode(preData[0]["articleid"]) : string.Empty) .Replace("<TITLE>", HttpUtility.HtmlEncode(title ?? (preDataRow != null ? preDataRow["title"] : string.Empty))) .Replace("<RELATIVE_PATH>", HttpUtility.HtmlEncode(relativeUrl ?? (preDataRow != null ? preDataRow["relative_url"] : string.Empty))) .Replace("<TAGS>", HttpUtility.HtmlEncode(tags ?? (preDataRow != null ? preDataRow["tags"] : string.Empty))) .Replace("<ALLOW_HTML>", allowHTML || (title == null && preDataRow != null && preDataRow["allow_html"].Equals("1")) ? "checked" : string.Empty) .Replace("<ALLOW_COMMENTS>", allowComments || (title == null && preDataRow != null && preDataRow["allow_comments"].Equals("1")) ? "checked" : string.Empty) .Replace("<SHOW_PANE>", showPane || (title == null && preDataRow != null && preDataRow["show_pane"].Equals("1")) ? "checked" : string.Empty) .Replace("<INHERIT>", inheritThumbnail || (title == null && preDataRow != null && preDataRow["thumbnailid"].Length > 0) ? "checked" : string.Empty) .Replace("<UPDATE_EXISTING>", updateExisting || (title == null && preDataRow != null) ? "checked" : string.Empty) .Replace("<BODY>", HttpUtility.HtmlEncode(body ?? (preDataRow != null ? preDataRow["body"] : string.Empty))) ; // Set flags // -- Update existing checkbox if ((permAdmin || permEdit) && preData != null) pageElements.setFlag("UPDATE_EXISTING"); // Finalize page Misc.Plugins.addHeaderJS(pageElements["URL"] + "/Content/JS/Article.js", ref pageElements); Misc.Plugins.addHeaderCSS(pageElements["URL"] + "/Content/CSS/Article.css", ref pageElements); Misc.Plugins.addHeaderCSS(pageElements["URL"] + "/Content/CSS/Common.css", ref pageElements); // Add includes Common.formatProvider_formatIncludes(request, response, conn, ref pageElements, true, true); pageElements["TITLE"] = "Articles - Editor"; }