/// <summary> /// Undo the last change of status of the article. /// </summary> /// <param name="article">An article whose status is to be advanced.</param> /// <param name="user">The user who is changing the status of the article.</param> /// <returns>True if the status of the article was succesfully undoed.</returns> public static bool SetArticleStatusUndo(WikiArticle article, User user) { if (article.Type == ArticleType.Code || article.Type == ArticleType.Site) throw new NotSupportedException(string.Format("This method cannot be used to update an article of type {0}.", article.Type.ToString())); if (!Authentication.CanUserUndoStatus(article, user) || article.Status <= ArticleStatus.Draft) return false; // Check rights. return UpdateArticleStatus(article, user, article.Status - 1, false); }
/// <summary> /// Set the status of the article to "Written". /// </summary> /// <param name="article">An article whose status is to be advanced.</param> /// <param name="user">The user who is changing the status of the article.</param> /// <returns>True if the status of the article was succesfully advanced.</returns> public static bool SetArticleToWritten(WikiArticle article, User user) { if (article.Type == ArticleType.Code || article.Type == ArticleType.Site) throw new NotSupportedException(string.Format("This method cannot be used to update an article of type {0}.", article.Type.ToString())); if (!Authentication.CanArticleBeCompletedByUser(article, user)) return false; // Check rights. // Release if the user doesn't have the rights to go on with the publication process. return UpdateArticleStatus(article, user, ArticleStatus.Written, user.Rights < UserRights.Reviewer); }
/// <summary> /// Set an article Public. This is achieved by moving its status from "Internal" to "ToBeWritten". /// </summary> /// <param name="article">An article to set Public.</param> /// <param name="user">The user updating the status of the article.</param> /// <returns>True if the status of the article was successfully updated.</returns> public static bool SetArticlePublic(WikiArticle article, User user) { // An article can only be set to "Public" from the "Internal" status. if (article.Status == ArticleStatus.Internal && user.CanCreate && Authentication.CanUserValidateArticle(article, user)) return UpdateArticleStatus(article, user, ArticleStatus.Draft, false); else return false; }
/// <summary> /// Updates the publicity of an articles (whether its Internal or Public). /// The publicity of an article is implemented by its status. /// Publicity == Internal ==> Status == Internal. /// Publicity == Public ==> Status >= ToBeWritten. /// </summary> /// <param name="article">An article whose publicity is updated.</param> /// <param name="user">The user that is switching the publicity of the article.</param> /// <param name="mustSetToPublic">True if the article publicity must be set to Public, False if the publicity must be set to Internal.</param> /// <returns>True if the publicity of the article was successfully updated.</returns> public static bool SetArticlePublicity(WikiArticle article, User user, bool mustSetToPublic) { return mustSetToPublic ? SetArticlePublic(article, user) : SetArticleInternal(article, user); }
/// <summary> /// Checks whether a user can set the status of a given article to "Reviewed". /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user can review the article.</returns> public static bool CanArticleBeReviewedByUser(WikiArticle article, User user) { return CouldUserReviewArticle(article, user) && article.LockedBy == user.UserLogin; }
/// <summary> /// Set an article Internal. This is achieved by setting its status to "Internal". /// </summary> /// <param name="article">An article.</param> /// <param name="user">The user updating the status of the article.</param> /// <returns>True if the status of the article was successfully updated.</returns> public static bool SetArticleInternal(WikiArticle article, User user) { if (article.Status == ArticleStatus.Internal) return true; if (user.CanCreate && Authentication.CanUserValidateArticle(article, user)) return UpdateArticleStatus(article, user, ArticleStatus.Internal, false); else return false; }
/// <summary> /// Checks whether a user could perform any editing or validating action on a given article if he/she had a lock on it. /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user could perform any action on the article if he/she had locked it.</returns> public static bool CouldUserValidateArticle(WikiArticle article, User user) { return (article.IsInternal && user.Rights >= UserRights.Writer) || CouldUserCompleteArticle(article, user) || CouldUserReviewArticle(article, user) || CouldUserApproveArticle(article, user); }
/// <summary> /// Update the status of an article. /// This method is private because it does not perform any user rights checks, which must be thus done before calling it. /// </summary> /// <param name="article">An article whose status is updated.</param> /// <param name="user">The user updating the status of an article.</param> /// <param name="articleStatus">The new status of the article.</param> /// <param name="mustReleaseLock">Indicates that the lock that user has on the article must be released.</param> /// <returns>True if the query was successful.</returns> private static bool UpdateArticleStatus(WikiArticle article, User user, ArticleStatus articleStatus, bool mustReleaseLock) { // No user rights are checked except that the article is not locked by another user. // The last modification user is also updated so that we can check whether the user can undo the article afterwards. string query = string.Format(@"UPDATE tbl_articles SET art_status = {2}, art_modification_user = '******', art_validation_date = GETDATE(), art_validation_user = '******', art_locked_by = '{3}' WHERE art_id_pkey = {0} AND (art_locked_by = '{1}' OR art_locked_by = '')", article.Id, user.UserLogin, (int)articleStatus, mustReleaseLock ? string.Empty : user.UserLogin); bool isSuccessful = Convert.ToBoolean(DataAccess.ExecuteOwnStatement(query)); if (isSuccessful) { // If the query was successful, update the current article. article.Status = articleStatus; if (mustReleaseLock) article.LockedBy = string.Empty; else article.LockedBy = user.UserLogin; } return isSuccessful; }
/// <summary> /// Checks whether a user could set the status of a given article to "Written" if he/she had a lock on it. /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user could complete the article if he/she had a lock on it.</returns> public static bool CouldUserCompleteArticle(WikiArticle article, User user) { return user.Rights >= UserRights.Writer && article.Status == ArticleStatus.Draft; }
/// <summary> /// Checks whether a user could set the status of a given article to "Reviewed" if he/she had a lock on it. /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user could review the article if he/she had a lock on it.</returns> public static bool CouldUserReviewArticle(WikiArticle article, User user) { return user.Rights >= UserRights.Reviewer && article.Status == ArticleStatus.Written; }
/// <summary> /// Checks whether a user could set the status of a given article to "Approved" if he/she had a lock on it. /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user could approve the article if he/she had locked it.</returns> public static bool CouldUserApproveArticle(WikiArticle article, User user) { return user.Rights >= UserRights.Approver && article.Status == ArticleStatus.Reviewed; }
/// <summary> /// Checks whether a user can perform any editing or validating action on a given article. /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user can perform any action on the article.</returns> public static bool CanUserValidateArticle(WikiArticle article, User user) { return article.LockedBy == user.UserLogin && CouldUserValidateArticle(article, user); }
/// <summary> /// Checks whether the user can cancel his/her last modification on the status of the article. /// This can only happen if he/she was the last one to perform an update on it. /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user can undo his last status change.</returns> public static bool CanUserUndoStatus(WikiArticle article, User user) { // The user musth ave editing rights, i.e. must be a writer or above. bool isUserEditor = user.Rights >= UserRights.Writer; // "Draft" articles cannot have their status reverted since they are the first status a public article can have. bool canArticleBeUndone = article.Status > ArticleStatus.Draft; // The user must be the last validator of the article. bool isUserLastValidator = article.ValidationUser == user.UserLogin; // We also check that the user was the last modifier, since he/she's not supposed to revert the status if someone else has made changes since. bool isUserLastModifier = article.LastModificationUser == user.UserLogin; // The article is not currently locked by someone else. bool isArticleLockedByAnotherUser = article.IsLocked && article.LockedBy != user.UserLogin; return isUserEditor && canArticleBeUndone && isUserLastModifier && isUserLastValidator && !isArticleLockedByAnotherUser; }
/// <summary> /// Update a database article after having copied its previous version to history (the ID of the article must already exist). /// </summary> /// <param name="article">A modified article to update in database based on its (already existing) ID.</param> /// <param name="user">The acronym of the user that made the update.</param> /// <param name="error">A reference parameter filled upon encountering error.</param> /// /// <returns>True if the article was correctly updated.</returns> public static bool UpdateArticleText(WikiArticle article, User user, ref string error) { // Check the type of the article. if (article.Type == ArticleType.Code || article.Type == ArticleType.Site) throw new NotSupportedException(string.Format("This method cannot be used to update an article of type {0}.", article.Type.ToString())); // Check user rights. if (!Authentication.CanUserValidateArticle(article, user)) { error = "You don't have the right to edit this article."; return false; } // Check that the article exists. if (article.Id <= 0 || !DoesArticleExist(article.Id)) { error = "The article does not exist."; return false; } // Copy previous version into history. // The following logic limits the number of entries in the history table to one for the same article on the same day by the same user. bool isUserLastEditor = article.LastModificationUser == user.UserLogin; bool hasDayPassed = Tools.GetNumberOfDaysSinceDate(article.LastModificationDate) >= 1; if (!isUserLastEditor || hasDayPassed) { string copyToHistoryQuery = string.Format(@"INSERT INTO tbl_article_history (ahi_art_id_fkey, ahi_title, ahi_text, ahi_history_date, ahi_history_user) SELECT art_id_pkey, art_title, art_text, GETDATE(), '{1}' FROM tbl_articles WHERE art_id_pkey = {0}", article.Id, user.UserLogin); DataAccess.ExecuteOwnStatement(copyToHistoryQuery); } // Limit the size of the summary to its maximal length. string articleSummary = WikiParser.GetSummary(article.Text); if (articleSummary.Length > Tools.SUMMARY_MAX_LENGTH) articleSummary = articleSummary.Remove(Tools.SUMMARY_MAX_LENGTH); // Updates the summary in the current Article. article.Summary = articleSummary; // Update the text, summary and modification information of the article. string query = string.Format(@"UPDATE tbl_articles SET art_text = '{1}', art_summary = '{2}', art_modification_date = GETDATE(), art_modification_user = '******' WHERE art_id_pkey = {0}", article.Id, Tools.TextToSQL(article.Text), Tools.TextToSQL(articleSummary), user.UserLogin); return Convert.ToBoolean(DataAccess.ExecuteOwnStatement(query)); }
/// <summary> /// Approve the article. This produces an approved read-only copy of the article and set the status of its latest running version to internal. /// </summary> /// <param name="article">An article whose status is to be advanced.</param> /// <param name="user">The user who is changing the status of the article.</param> /// <param name="error">A string that can be used to feed information back to the caller.</param> /// <returns>True if the status of the article was succesfully advanced.</returns> public static bool ApproveArticle(WikiArticle article, User user, ref string error) { // Check article Type. if (article.Type == ArticleType.Code || article.Type == ArticleType.Site) throw new NotSupportedException(string.Format("This method cannot be used to update an article of type {0}.", article.Type.ToString())); // Check the the article exists in DB. if (article.Id <= 0 || !DoesArticleExist(article.Id)) { error = "The article does not exist"; return false; } // Check that the user can approve the article right now. if (!Authentication.CanArticleBeApprovedByUser(article, user)) { error = "You cannot approve this article now, either because you haven't locked the article or you lack the rights to."; return false; } // Save an approved copy of the article. // The content of the article can still be modified, but its approved copies will remain unchanged. string query = string.Format(@"INSERT INTO tbl_article_approved (app_art_id_fkey, app_title, app_type, app_text, app_summary, app_approval_date, app_approval_user) SELECT art_id_pkey, art_title, art_type, art_text, art_summary, GETDATE(), '{1}' FROM tbl_articles WHERE art_id_pkey = {0}", article.Id, user.UserLogin); bool isSuccessful = Convert.ToBoolean(DataAccess.ExecuteOwnStatement(query)); // Update the status of the article to internal so it gets out of the validation process and can still be edited. isSuccessful = isSuccessful && UpdateArticleStatus(article, user, ArticleStatus.Internal, true); return isSuccessful; }
/// <summary> /// Insert a new article in database, with preliminary verification that it does not exist yet. /// </summary> /// <param name="articleTitle">The title of the new article (that will be checked for duplicates).</param> /// <param name="articleText">The text of the new article.</param> /// <param name="articleType">The type of the new article.</param> /// <param name="user">The user who is creating the article.</param> /// <param name="error">A reference parameter filled upon encountering an error.</param> /// <returns>The new article if the article was correctly created or null otherwise.</returns> public static WikiArticle CreateArticle(string articleTitle, ArticleType articleType, string articleText, User user, ref string error) { if (articleType == ArticleType.Code || articleType == ArticleType.Site) throw new NotSupportedException(string.Format("This method cannot be used to create an article of type {0}.", articleType.ToString())); // Check user rights. if (!user.CanCreate) { error = "You don't have the right to create articles."; return null; } // Check that the article doesn't exist yet. if (DoesArticleExist(articleTitle, articleType)) { error = "An article with the same title already exists."; return null; } // Limit the size of the summary to its maximal length. string articleSummary = WikiParser.GetSummary(articleText); if (articleSummary.Length > Tools.SUMMARY_MAX_LENGTH) articleSummary = articleSummary.Remove(Tools.SUMMARY_MAX_LENGTH); string query = string.Format(@"INSERT INTO tbl_articles (art_title, art_text, art_type, art_creation_date, art_creation_user, art_modification_date, art_modification_user, art_status, art_locked_by, art_summary) VALUES ('{0}', '{1}', {2}, GETDATE(), '{3}', GETDATE(), '{3}', {4}, '{3}', '{5}')", Tools.TextToSQL(articleTitle), Tools.TextToSQL(articleText), (int)articleType, user.UserLogin, (int)ArticleStatus.Internal, Tools.TextToSQL(articleSummary)); DataAccess.ExecuteOwnStatement(query); return GetArticle(articleType, articleTitle); }
/// <summary> /// Lock the article for the user. /// </summary> /// <param name="article">An article to lock.</param> /// <param name="user">The user that locks the article.</param> /// <returns>True if a lock was successfully applied on the article for the user.</returns> public static bool LockArticle(WikiArticle article, User user) { if (!Authentication.CouldUserValidateArticle(article, user)) return false; // Check rights. string query = string.Format(@"UPDATE tbl_articles SET art_locked_by = '{1}' WHERE art_id_pkey = {0} AND art_locked_by = ' '", article.Id, user.UserLogin); bool isSuccessful = Convert.ToBoolean(DataAccess.ExecuteOwnStatement(query)); if (isSuccessful) article.LockedBy = user.UserLogin; // If the query was successful, update the current article. return isSuccessful; }
/// <summary> /// Gets a list of the articles currently locked by a given user. /// </summary> /// <param name="user">The user that has locked articles.</param> /// <returns>A list of articles locked by the user.</returns> public static List<WikiArticleInfo> GetArticlesLockedBy(User user) { string query = string.Format(@"SELECT {1} FROM tbl_articles WHERE art_locked_by = '{0}' ORDER BY art_title", user.UserLogin, ARTICLE_INFO_FIELDS); return GetArticleInfosFromQuery(query); }
/// <summary> /// Checks whether a user can set the status of a given article to "Written". /// </summary> /// <param name="article">An article.</param> /// <param name="user">A user.</param> /// <returns>True if the user can complete the article.</returns> public static bool CanArticleBeCompletedByUser(WikiArticle article, User user) { return CouldUserCompleteArticle(article, user) && article.LockedBy == user.UserLogin; }