/// <summary> /// Checks whether the current 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> /// <returns>True if the user can undo his last status change.</returns> public static bool CanCurrentUserUndoStatus(WikiArticle article) { return CanUserUndoStatus(article, CurrentUser); }
/// <summary> /// Checks whether an article has an approved version, i.e. it has a validated copy visible to external users. /// </summary> /// <param name="article">An article.</param> /// <returns>True if there exists an approved version of the article.</returns> public static bool DoesArticleHaveApprovedVersion(WikiArticle article) { return DoesArticleHaveApprovedVersion(article.Type, article.Title); }
/// <summary> /// Page load. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Arguments</param> protected void Page_Load(object sender, EventArgs e) { try { string articleFullTitle = string.Empty; if (!Page.IsPostBack) { // Title. string urlArticleTitle = Request.QueryString["page"]; if (!string.IsNullOrEmpty(urlArticleTitle)) { articleFullTitle = Tools.UrlToText(urlArticleTitle); this.hdnArticleFullTitle.Value = articleFullTitle; } } else { articleFullTitle = this.hdnArticleFullTitle.Value; } // Rights: The user has only external rights => Redirect. if (!Authentication.CanCurrentUserSeeInternalContent) if (!string.IsNullOrEmpty(articleFullTitle) && DataModule.DoesArticleHaveApprovedVersion(articleFullTitle)) Response.Redirect(Tools.GetUrlToRead(articleFullTitle)); else Response.Redirect(Tools.DEFAULT_PAGE); if (!string.IsNullOrEmpty(articleFullTitle) && DataModule.DoesArticleExist(articleFullTitle)) { #region Update Master.PageAction = PageAction.Edit; Master.ArticleTitle = articleFullTitle; this.m_Article = DataModule.GetArticle(articleFullTitle); if (this.m_Article == null) throw new Exception("The article exists but could not be retrieved."); // Are we editing a section? //m_Section = Convert.ToInt32(Request.QueryString["section"]); if (!int.TryParse(Request.QueryString["section"], out m_Section)) m_Section = -1; if (!Page.IsPostBack) { if (m_Section > 0) this.tbxArticleText.Text = WikiParser.GetSectionText(this.m_Article.Text, m_Section); else this.tbxArticleText.Text = this.m_Article.Text; } this.lblPageAction.Text = this.Master.PageAction.ToString(); this.lblArticleTitle.Text = this.m_Article.TitleWithType; this.tbxArticleTitle.Visible = false; #endregion Update } else { if (!Authentication.CurrentUser.CanCreate) Response.Redirect(Tools.DEFAULT_PAGE); #region Create Master.PageAction = PageAction.Create; Master.ArticleTitle = string.Empty; if (!Page.IsPostBack) { // Extract the title and type from the full title. // Note that these methods give default parameters if needed. string articleTitle = Tools.GetArticleTitle(articleFullTitle); if (articleTitle.Length == 0) articleTitle = DEFAULT_EDITOR_TITLE; ArticleType articleType = Tools.GetArticleType(articleFullTitle); this.lblPageAction.Text = this.Master.PageAction.ToString(); this.lblArticleType.Text = articleType.ToString(); this.lblArticleTitle.Visible = false; this.tbxArticleTitle.Visible = true; this.tbxArticleTitle.Text = articleTitle; this.tbxArticleText.Text = string.Format(DEFAULT_EDITOR_TEXT_FORMAT, articleTitle); } #endregion Create } #region Rights // Note that if the user has no editing rights, he was redirected above in this method. if (this.m_Article == null && Authentication.CurrentUser.CanCreate) { // The user can create and is creating the article. this.btnSave.ToolTip = "Save your modifications and go back to the Read page. This does not automatically remove the lock you have on the article."; this.btnCancel.ToolTip = "Cancel your modifications and go back to the Read page. This does not automatically remove the lock you have on the article."; this.cbxUnlock.Text = "Also free the article?"; this.cbxUnlock.ToolTip = "Additionally remove yourself as the current editor thus enabling someone else to edit it."; this.btnSave.Visible = true; this.btnCancel.Visible = true; this.cbxUnlock.Visible = true; } else if (Authentication.CouldCurrentUserValidateArticle(this.m_Article)) { // The user has editing rights that could allow her to perform some action if the article is not locked yet. if (this.m_Article.IsLocked) { // The article is locked, but by whom? if (this.m_Article.LockedBy == Authentication.CurrentUser.UserLogin) { // The article is locked by the current user. this.btnSave.ToolTip = "Save your modifications and go back to the Read page. This does not automatically remove the lock you have on the article."; this.btnCancel.ToolTip = "Cancel your modifications and go back to the Read page. This does not automatically remove the lock you have on the article."; this.cbxUnlock.Text = "Also free the article?"; this.cbxUnlock.ToolTip = "Additionally remove yourself as the current editor thus enabling someone else to edit it."; this.btnSave.Visible = true; this.btnCancel.Visible = true; this.cbxUnlock.Visible = true; } else { // The article is locked by another user. this.lblPageAction.Text = "Source"; this.divEditorTop.Visible = false; this.tbxArticleText.ReadOnly = true; User lockedByUser = DataModule.GetInternalUser(this.m_Article.LockedBy); //int nbOfDaysOfLock = Tools.GetNumberOfDaysSinceDate(this.m_Article.LockedAt); this.lblStatusInformation.Text = string.Format("This article is currently being edited by <b>{0}</b>. Last modification <b>{1}</b>.", lockedByUser.FullName, Tools.GetDateAndTimeText(this.m_Article.LastModificationDate)); } } else { // The article is not locked. Offer him/her to lock it. this.lblPageAction.Text = "Source"; this.divEditorTop.Visible = false; this.tbxArticleText.ReadOnly = true; //this.lblStatusInformation.Text = "This article is currently readonly"; this.btnLock.ToolTip = "Take this article for edit and prevent someone else to modify it."; this.btnLock.Visible = true; } } else { // The user has some editing rights but the article publication process is further ahead than her/his rights allow. // Note that if the user has no editing rights, s/he was redirected at the beginning of this method. this.lblPageAction.Text = "Source"; this.divEditorTop.Visible = false; this.tbxArticleText.ReadOnly = true; //this.tbxArticleText.CssClass += " faded"; this.lblStatusInformation.Text = "This article is undergoing its publication process beyond your editing rights."; } #endregion Rights } catch (Exception ex) { string javascriptCall = string.Format(@"displayErrorMessage('lblErrorMessage', '{0}');", Tools.TextToJavascript(ex.Message)); Page.ClientScript.RegisterStartupScript(this.GetType(), "displayErrorMessage", javascriptCall, true); } }
/// <summary> /// Updates the type of an article. /// </summary> /// <param name="article"></param> /// <param name="articleNewType"></param> /// <returns></returns> public static bool UpdateArticleType(WikiArticle article, ArticleType articleNewType) { // Check that the new type is not Code or Site, since article of those types are generated automatically and cannot be set manually. if (articleNewType == ArticleType.Code || articleNewType == ArticleType.Site) throw new NotSupportedException(string.Format("The title of articles of type {0} cannot be edited.", articleNewType.ToString())); // Checks that there is not already an article with the same type in the target new category. if (DataModule.DoesIdenticalArticleExist(Tools.GetArticleFullTitle(articleNewType, article.Title), article.Id)) return false; // Refresh the type of the current article. article.Type = articleNewType; // Update the type of the article in DB. string query = string.Format("UPDATE tbl_articles SET art_type = '{0}' WHERE art_id_pkey = {1}", (int)articleNewType, article.Id); return Convert.ToBoolean(DataAccess.ExecuteOwnStatement(query)); }
/// <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> /// 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> /// 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> /// Checks whether the current user could set the status of a given article as "Reviewed" if he/she had a lock on it. /// </summary> /// <param name="article">An article.</param> /// <returns>True if the current user could review the article if he/she had a lock on it.</returns> public static bool CouldCurrentUserReviewArticle(WikiArticle article) { return CouldUserReviewArticle(article, CurrentUser); }
/// <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 the current user could set the status of a given article as "Approved" if he/she had a lock on it. /// </summary> /// <param name="article">An article.</param> /// <returns>True if the current user could approve the article if he/she had locked it.</returns> public static bool CouldCurrentUserApproveArticle(WikiArticle article) { return CouldUserApproveArticle(article, CurrentUser); }
/// <summary> /// Checks whether the current user could set the status of a given article as "Written" if he/she had a lock on it. /// </summary> /// <param name="article">An article.</param> /// <returns>True if the current user could complete the article if he/she had a lock on it.</returns> public static bool CouldCurrentUserCompleteArticle(WikiArticle article) { return CouldUserCompleteArticle(article, CurrentUser); }
/// <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> /// Checks whether the current user can perform any editing or validating action on a given article. /// </summary> /// <param name="article">An article.</param> /// <returns>True if the user can perform any action on the article.</returns> public static bool CanCurrentUserValidateArticle(WikiArticle article) { return article.LockedBy == CurrentUser.UserLogin && CouldUserValidateArticle(article, CurrentUser); }
/// <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 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> /// 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> /// 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> /// Remove any current lock on a given article. /// </summary> /// <param name="article">An article to unlock.</param> /// <returns>True if the article was correctly unlocked.</returns> public static bool UnlockArticle(WikiArticle article) { string query = string.Format(@"UPDATE tbl_articles SET art_locked_by = '' WHERE art_id_pkey = {0} AND art_locked_by <> ' '", article.Id); bool isSuccessful = Convert.ToBoolean(DataAccess.ExecuteOwnStatement(query)); if (isSuccessful) article.LockedBy = string.Empty; // If the query was successful, update the current article. return isSuccessful; }
/// <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> /// Updates the title of an existing article. /// </summary> /// <param name="article">An article to update.</param> /// <param name="articleNewTitle">The new title of the article.</param> /// <returns>True if the title of the article could be changed. False if the title conflicted with another article.</returns> public static bool UpdateArticleTitle(WikiArticle article, string articleNewTitle) { string query = string.Format(@"UPDATE tbl_articles SET art_title = '{0}' WHERE art_id_pkey = {1}", Tools.TextToSQL(articleNewTitle), article.Id); 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> /// Updates the parent categories of an article. The previous parent categories are entirely replaced with the ones provided. /// </summary> /// <param name="article">An article to update.</param> /// <param name="newCategories">The list of update categories of the given article.</param> /// <returns>True if the query was successful.</returns> public static bool UpdateParentCategories(WikiArticle article, List<WikiArticleInfo> newCategories) { string query = string.Empty; if (newCategories == null) return false; // If there are no categories in the collection, we can delete every link with this article and return. if (newCategories.Count == 0) { query = string.Format("DELETE FROM tbl_article_hierarchy WHERE ahe_child_art_fkey = {0}", article.Id); return Convert.ToBoolean(DataAccess.ExecuteOwnStatement(query)); } // If the article is a Category, check that it's not already included somewhere in its parent hierarchy. // You can't make a Category a child of itself or of other categories having the Category as a parent because it would cause loops in the hiearchy. if (article.Type == ArticleType.Category && IsCategoryInCategoriesHierarchy(article, newCategories)) return false; string updatedCategoryIds = string.Empty; foreach (WikiArticleInfo category in newCategories) { // If the link between the article and the category does not exist yet, create it. if (!IsArticleInCategory(article.Id, category.Id)) { query = string.Format("INSERT INTO tbl_article_hierarchy (ahe_child_art_fkey, ahe_parent_art_fkey) VALUES ({0}, {1})", article.Id, category.Id); DataAccess.ExecuteOwnStatement(query); } // At the same time, keep track of the category indexes => in the end all the categories but those will be deleted. updatedCategoryIds += category.Id + ","; } updatedCategoryIds = updatedCategoryIds.Remove(updatedCategoryIds.Length - 1); // Remove trailing comma. // Remove every category link that was not in the categories to save. query = string.Format("DELETE FROM tbl_article_hierarchy WHERE ahe_child_art_fkey = {0} AND ahe_parent_art_fkey NOT IN ({1})", article.Id, updatedCategoryIds); return DataAccess.ExecuteOwnStatement(query) >= 0; // There might be nothing to delete (nothing unchecked in the GUI). }
/// <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> /// Deletes every occurence of the article and its relations in database. /// </summary> /// <param name="article">An article to delete.</param> /// <returns>True if every step of the deletion was successful.</returns> public static bool DeleteArticle(WikiArticle article) { // [KDE]: Such a method is too powerful. Only use locally for debugging purposes. #if DEBUG // Delete the article hiearchy. string query = string.Format("DELETE FROM tbl_article_hierarchy WHERE ahe_child_art_fkey = {0} OR ahe_parent_art_fkey = {0}", article.Id); DataAccess.ExecuteOwnStatement(query); // Delete the article history. query = string.Format("DELETE FROM tbl_article_history WHERE ahi_art_id_fkey = {0}", article.Id); DataAccess.ExecuteOwnStatement(query); // Delete the article approved history. query = string.Format("DELETE FROM tbl_article_approved WHERE app_art_id_fkey = {0}", article.Id); DataAccess.ExecuteOwnStatement(query); // Delete the article query = string.Format("DELETE FROM tbl_articles WHERE art_id_pkey = {0}", article.Id); return DataAccess.ExecuteOwnStatement(query) > 0; #else return false; #endif }
/// <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> /// Page load. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Arguments.</param> protected void Page_Load(object sender, EventArgs e) { // Get the title of the article. string articleFullTitle = string.IsNullOrEmpty(Request.QueryString["page"]) ? string.Empty : Tools.UrlToText(Request.QueryString["page"]); // Get the version of the article that must be be shown. string articleVersionText = Request.QueryString["version"]; RequestedArticleVersion articleVersion = Authentication.CanCurrentUserSeeInternalContent ? Tools.ParseArticleVersion(articleVersionText) : RequestedArticleVersion.Approved; // Public can only see approved. bool mustUseApprovedVersion = articleVersion == RequestedArticleVersion.Approved || articleVersion == RequestedArticleVersion.ApprovedByLatest; #region Get Article if (articleFullTitle.Length == 0) { Response.Redirect(Tools.DEFAULT_PAGE); } else if (articleFullTitle == Tools.RANDOM_PAGE_KEYWORD) { #region Random this.m_Article = DataModule.GetRandomArticle(); // Redirect the user to a random article page. // We can't simply load a random article into the interface because the title of the article would be "random" // and the menu links would not work properly. if (this.m_Article == null) Response.Redirect(Tools.DEFAULT_PAGE); else Response.Redirect("./Read.aspx?page=" + Tools.TextToUrl(this.m_Article.FullTitle)); #endregion Random } else if (articleFullTitle == Tools.LATEST_PAGE_KEYWORD) { #region Latest this.m_Article = DataModule.GetLatestArticle(); // Redirect the user to the read page of the latest modified article. // We can't simply load the latest article into the interface because the title of the article would be "latest" // and the menu links would not work properly. if (this.m_Article == null) Response.Redirect(Tools.DEFAULT_PAGE); else Response.Redirect("./Read.aspx?page=" + Tools.TextToUrl(this.m_Article.FullTitle)); #endregion Latest } else if (Tools.GetArticleType(articleFullTitle) == ArticleType.Code) { #region Code LoadCodeArticle(Tools.GetArticleTitle(articleFullTitle)); return; #endregion Code } else if (Tools.GetArticleType(articleFullTitle) == ArticleType.Site) { #region Site LoadSiteArticle(Tools.GetArticleTitle(articleFullTitle)); return; #endregion Site } else if (DataModule.DoesArticleExist(articleFullTitle)) { #region Wiki Article // Should we get the latest version or approved version of the article? And if so, by the title of the article or the title of its approved version? bool doesApprovedVersionExist = (articleVersion == RequestedArticleVersion.Approved || articleVersion == RequestedArticleVersion.LatestByApproved) ? DataModule.DoesApprovedArticleExist(articleFullTitle) : DataModule.DoesArticleHaveApprovedVersion(articleFullTitle); if (!Authentication.CanCurrentUserSeeInternalContent) { if (doesApprovedVersionExist) this.m_Article = DataModule.GetApprovedArticle(articleFullTitle); // External can only see approved content. else Response.Redirect(Tools.DEFAULT_PAGE); // No approved version => nothing to display. } else if (mustUseApprovedVersion && doesApprovedVersionExist) { if (articleVersion == RequestedArticleVersion.Approved) this.m_Article = DataModule.GetApprovedArticle(articleFullTitle); else this.m_Article = DataModule.GetApprovedArticleByLatest(articleFullTitle); } else if (articleVersion == RequestedArticleVersion.LatestByApproved) this.m_Article = DataModule.GetArticleByApproved(articleFullTitle); else this.m_Article = DataModule.GetArticle(articleFullTitle); // Latest version OR no Approved version existed for the given title. #endregion Wiki Article } #endregion Get Article #region Missing Article if (this.m_Article == null) { // No article matching the title was found. if (Authentication.CanCurrentUserSeeInternalContent && Authentication.CurrentUser.CanCreate) { // Offer to the user the possibility to create one. articleFullTitle = Tools.GetArticleFullTitle(articleFullTitle); // Makes sure the type exists and changes it to default if it does not. this.lblArticleTitle.Text = string.Format(@"Create ""{0}""?", articleFullTitle); this.lblArticleContent.Text = string.Format(@"<p>The page you are trying to read does not exist. Would you like to create a new page ""{0}""?</p>", Tools.GetLinkToCreate(articleFullTitle)); } else { // Feedback and next action. ArticleType articleType = Tools.GetArticleType(articleFullTitle); articleFullTitle = Tools.GetArticleTitle(articleFullTitle); this.lblArticleTitle.Text = string.Format(@"{0} ""{1}"" does not exist.", articleType, articleFullTitle); this.lblArticleContent.Text = string.Format("<p>The page you are trying to read does not exist.</p>"); } this.lblArticleContent.Text += string.Format("<p>Go back to the {0}.</p>", Tools.GetLinkToUrl("./Home.aspx", "Home page")); this.lblArticleContent.Text += string.Format("<p>Go back to the {0}.</p>", Tools.GetLinkToUrl("./Index.aspx", "Index")); this.loaParentCategories.Visible = false; this.frmStatus.Visible = false; // Remove "Export" link in Master page. Master.PageAction = PageAction.Read404; Master.ArticleTitle = articleFullTitle; return; } #endregion Missing Article // Home page to be treated by Home.aspx. if(this.m_Article.Type == ArticleType.Homepage) Response.Redirect(Tools.GetUrlToHome()); // Master Page. if (this.m_Article.Status == ArticleStatus.Approved) Master.PageAction = PageAction.ReadApproved; else Master.PageAction = PageAction.Read; Master.ArticleTitle = this.m_Article == null ? articleFullTitle : this.m_Article.FullTitle; Master.PageTitle = this.m_Article == null ? articleFullTitle : this.m_Article.TitleWithType; #region Title // Set the main (h1) title that is displayed at the top of the page using the type and title of the article. // A link to see the approved or normal version of the article may also be provided whereas that applies. string articleTypeHtml = this.m_Article.Type == Tools.DEFAULT_ARTICLE_TYPE ? string.Empty : string.Format(@" <span class=""faded"">({0})</span>", this.m_Article.Type.ToString()); string articleTitleHtml = string.Format(@"{0}{1}", this.m_Article.Title, articleTypeHtml); this.lblArticleTitle.Text = articleTitleHtml;// +Environment.NewLine; if (Authentication.CurrentUser.Rights >= UserRights.Reader) { // If the user is an internal user, he can access both versions of the article (normal and approved). // External user can only access the approved version (if any). if (this.m_Article.Status == ArticleStatus.Approved) { string linkToLatestVersion = Tools.GetLinkToRead(this.m_Article.FullTitle, "Link to latest version", RequestedArticleVersion.LatestByApproved); string linkToLatestVersionSpan = string.Format(@"<span class=""title_link"">{0}</span>", linkToLatestVersion); this.lblArticleTitle.Text += linkToLatestVersionSpan; } else if (DataModule.DoesArticleHaveApprovedVersion(this.m_Article)) { string linkToApprovedVersion = Tools.GetLinkToRead(this.m_Article.FullTitle, "Link to approved version", RequestedArticleVersion.ApprovedByLatest); string linkToApprovedVersionSpan = string.Format(@"<span class=""title_link"">{0}</span>", linkToApprovedVersion); this.lblArticleTitle.Text += linkToApprovedVersionSpan; } } #endregion Title #region Content // Check if the current user has the priviledge to edit the article right now - if he had locked it. // Setting the article title to empty in the wiki parser will prevent the display of an "[Edit]" link for each section. if (Authentication.CouldCurrentUserValidateArticle(this.m_Article)) this.lblArticleContent.Text = WikiParser.Parse(this.m_Article.Text, this.m_Article.FullTitle, true, Request.Url.Host, Request.ApplicationPath); else this.lblArticleContent.Text = WikiParser.Parse(this.m_Article.Text, string.Empty, true, Request.Url.Host, Request.ApplicationPath); #endregion Content #region Category Children if (this.m_Article.Type == ArticleType.Category) { // Child articles & categories. if (this.m_Article.Children != null) { List<WikiArticleInfo> childPages = mustUseApprovedVersion ? this.m_Article.ApprovedChildren : this.m_Article.Children; this.coaChildPages.Title = string.Format(@"Pages in category {0}", this.m_Article.Title); this.coaChildPages.SubTitle = string.Format("The following {0} in this category.", childPages.Count > 1 ? childPages.Count.ToString() + " pages are" : "page is"); this.coaChildPages.WikiArticles = childPages; this.coaChildPages.MustShowType = true; this.coaChildPages.MustShowCheckboxes = false; this.coaChildPages.MustUseApprovedVersion = mustUseApprovedVersion; } } #endregion Category Children #region Parent Categories List<WikiArticleInfo> parentCategories = mustUseApprovedVersion ? this.m_Article.ParentApprovedCategories : this.m_Article.ParentCategories; if (parentCategories != null) { int nbOfCategories = this.m_Article.ParentCategories.Count; if (Authentication.CanCurrentUserSeeInternalContent) if (mustUseApprovedVersion) this.loaParentCategories.Title = "Approved Categories"; else this.loaParentCategories.Title = Tools.GetLinkToProperties(this.m_Article.FullTitle + "#Categories", "Categories"); else this.loaParentCategories.Title = "Categories"; this.loaParentCategories.SubTitle = "The categories to which this page belongs."; this.loaParentCategories.WikiArticles = parentCategories; if (mustUseApprovedVersion) this.loaParentCategories.MustUseApprovedVersion = true; } else this.loaParentCategories.Visible = false; #endregion Parent Categories #region Status if (Authentication.CanCurrentUserSeeInternalContent)// && this.m_Article.Status != ArticleStatus.Approved) { switch (this.m_Article.Status) { case ArticleStatus.Internal: this.imgStatus.ImageUrl = "../Images/Icons/key.png"; break; case ArticleStatus.Approved: this.imgStatus.ImageUrl = "../Images/Icons/accept.png"; break; default: this.imgStatus.ImageUrl = "../Images/Icons/page_edit.png"; break; // page_go.png } this.imgStatus.ToolTip = this.m_Article.StatusDescription; this.lblStatus.Text = this.m_Article.StatusText; this.lblStatus.ToolTip = this.m_Article.StatusDescription; if (Authentication.CurrentUser.Rights == UserRights.Reader) this.lblStatusDescription.Text = this.m_Article.StatusDescription + "."; else if (Authentication.CurrentUser.Rights >= UserRights.Writer && this.m_Article.Status != ArticleStatus.Approved) { #region Writer // The user is a writer or above. He/she can see locking information and possibly advance the status of the article. if (Authentication.CouldCurrentUserValidateArticle(this.m_Article)) { this.lblSeparator.Visible = true; this.imgLock.Visible = true; // The user has the appropriate rights to validate the article. But it could be locked by someone else! if (this.m_Article.IsLocked && this.m_Article.LockedBy == Authentication.CurrentUser.UserLogin) { // The user has locked the article and can perform some editing action on it. this.imgLock.ImageUrl = "../Images/Icons/lock_edit.png"; this.lblLockDescription.Text = "You are the current editor of this article."; this.btnUnlock.ToolTip = "Release the article to allow someone else to work on it."; this.btnUnlock.Visible = true; if (this.m_Article.IsPublic) { if (Authentication.CouldCurrentUserCompleteArticle(this.m_Article)) { this.btnWritten.ToolTip = "Mark this article as completely written and send it for review."; this.btnWritten.Visible = true; } if (Authentication.CouldCurrentUserReviewArticle(this.m_Article)) { this.btnReviewed.ToolTip = "Mark this article as reviewed and send it for approval."; this.btnReviewed.Visible = true; } if (Authentication.CouldCurrentUserApproveArticle(this.m_Article)) { this.btnApproved.ToolTip = "Approve the article for public usage. Caution: this will enables public users to read the article."; this.btnApproved.Visible = true; } } } else if (this.m_Article.IsLocked && this.m_Article.LockedBy != Authentication.CurrentUser.UserLogin) { // The user COULD lock the article IF it was not locked by someone else. this.imgLock.ImageUrl = "../Images/Icons/lock_delete.png"; this.imgLock.ToolTip = "This article is being locked by someone else"; User currentEditor = DataModule.GetInternalUser(this.m_Article.LockedBy); this.lblLockDescription.Text = string.Format("This article is currently being edited by {0}.", currentEditor.FullName); } else { // The user CAN lock the article AS it is not locked. this.imgLock.ImageUrl = "../Images/Icons/lock_open.png"; this.lblLockDescription.Text = "You may take this article for exclusive edition."; this.btnLock.ToolTip = "Take this article for edit and prevent someone else to modify it."; this.btnLock.Visible = true; } } else { // The user cannot do anything because he/she doesn't have the rights to perform any edit at this point. this.imgStatus.ImageUrl = "../Images/Icons/page_delete.png"; this.lblStatusDescription.Text = "This article is going on its validation process beyond your rights."; } if (Authentication.CanCurrentUserUndoStatus(this.m_Article)) { // The user was the last one to perform an action. He/she may thus cancel his/her last validation action. this.lblLockDescription.Text = " You may cancel your last status change."; this.btnUndo.ToolTip = "Cancel your last change to the status of this article. You may only do this as long as it is not locked or modified by someone else."; this.btnUndo.Visible = true; } #endregion Writer } } else { // The user is an external user. Don't display any information about the status of articles to public. frmStatus.Visible = false; // Hides the whole form. } #endregion Status #region Footer // The footer is located below the article frame. // Display the time and user of last modification (only to internal users). if (Authentication.CanCurrentUserSeeInternalContent && this.m_Article.Status != ArticleStatus.Approved) { User lastModificationUser = DataModule.GetInternalUser(this.m_Article.LastModificationUser); this.lblPageFooter.Text = string.Format(@"Last modified {0} by <i>{1}</i>", Tools.GetDateAndTimeText(this.m_Article.LastModificationDate), lastModificationUser.FullName); } // [KDE]: Debug info. //if (Authentication.CurrentUser.UserName == "KDE") //{ // lblPageFooter.Text += string.Format("{0}Host = {1}{0}ApplicationPath = {2}{0}", "<br />", Request.Url.Host, Request.ApplicationPath); //} #endregion Footer }
/// <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> /// Handles a click on the Save button. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Arguments.</param> protected void btnSave_Click(object sender, EventArgs e) { try { string articleFullTitle = this.hdnArticleFullTitle.Value; string articleText = this.tbxArticleText.Text; string error = string.Empty; // Check the HTML syntax. //HtmlParser.CheckHtml(newText, out error); //if (!string.IsNullOrEmpty(error)) // throw new Exception("The article cannot be saved because the html code isn't well formed: " + error); if (Master.PageAction == PageAction.Create) { #region Create string articleNewTitle = this.tbxArticleTitle.Text.Trim(); // Check that the article title provided is not empty (or left to default). if (string.IsNullOrEmpty(articleNewTitle) || articleNewTitle == DEFAULT_EDITOR_TITLE) throw new Exception("Please set the title of the article before saving."); // Punish the user for trying to enter an invalid title. if (articleNewTitle.Contains(":")) throw new Exception("Colon (:) is not a valid character within an article title."); ArticleType articleType = Tools.GetArticleType(articleFullTitle); // The type of the article is indicated by the prefix in the title (e.g. "Category:"). articleFullTitle = Tools.GetArticleFullTitle(articleType, articleNewTitle); WikiArticle newArticle = DataModule.CreateArticle(articleNewTitle, articleType, articleText, Authentication.CurrentUser, ref error); if (ReferenceEquals(newArticle, null)) throw new Exception("The article could not be created: " + error); else this.m_Article = newArticle; #endregion Create } else { #region Update // Article or article section? if (this.m_Section < 0) this.m_Article.Text = articleText; else this.m_Article.Text = WikiParser.GetTextWithReinsertedSectionText(this.m_Article.Text, this.m_Section, articleText); // Update the article. if (!DataModule.UpdateArticleText(this.m_Article, Authentication.CurrentUser, ref error)) throw new Exception("The article could not be updated: " + error); #endregion Update } // Unlock the article if necessary. if (this.cbxUnlock.Checked) DataModule.UnlockArticle(this.m_Article); // The page is now saved. // Redirect either to the Read or Home page, if possible to the specific section from which the user comes from. if (Tools.GetArticleType(articleFullTitle) == ArticleType.Homepage) Response.Redirect(Tools.GetUrlToHome(this.m_Section)); else Response.Redirect(Tools.GetUrlToRead(articleFullTitle, this.m_Section)); } catch (Exception ex) { string javascriptCall = string.Format(@"displayErrorMessage('lblErrorMessage', '{0}');", Tools.TextToJavascript(ex.Message)); Page.ClientScript.RegisterStartupScript(this.GetType(), "displayErrorMessage", javascriptCall, true); } }
/// <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; }