using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using ScrewTurn.Wiki.PluginFramework;

namespace ScrewTurn.Wiki {

	public partial class AdminPages : BasePage {

		/// <summary>
		/// The numer of items in a page.
		/// </summary>
		public const int PageSize = 50;

		private IList<PageInfo> currentPages = null;

		private int rangeBegin = 0;
		private int rangeEnd = PageSize - 1;
		private int selectedPage = 0;

		private PageInfo externallySelectedPage = null;

		protected void Page_Load(object sender, EventArgs e) {
			AdminMaster.RedirectToLoginIfNeeded();

			if(!Page.IsPostBack) {
				// Load namespaces

				// Add root namespace
				lstNamespace.Items.Add(new ListItem("<root>", ""));

				List<NamespaceInfo> namespaces = Pages.GetNamespaces();

				foreach(NamespaceInfo ns in namespaces) {
					lstNamespace.Items.Add(new ListItem(ns.Name, ns.Name));
				}

				bool loaded = LoadExternallySelectedPage();

				if(!loaded) {
					// Load pages
					ResetPageList();
					rptPages.DataBind();
				}
			}
		}

		/// <summary>
		/// Resets the page list.
		/// </summary>
		private void ResetPageList() {
			currentPages = GetPages();
			pageSelector.ItemCount = currentPages.Count;
			pageSelector.SelectPage(0);
		}

		/// <summary>
		/// Gets the pages in the selected namespace.
		/// </summary>
		/// <returns>The pages.</returns>
		private IList<PageInfo> GetPages() {
			NamespaceInfo nspace = Pages.FindNamespace(lstNamespace.SelectedValue);
			List<PageInfo> pages = Pages.GetPages(nspace);

			List<PageInfo> result = new List<PageInfo>(pages.Count);

			string filter = txtFilter.Text.Trim().ToLower(System.Globalization.CultureInfo.CurrentCulture);

			foreach(PageInfo page in pages) {
				if(NameTools.GetLocalName(page.FullName).ToLower(System.Globalization.CultureInfo.CurrentCulture).Contains(filter)) {
					result.Add(page);
				}
			}

			return result;
		}

		/// <summary>
		/// Loads the externally selected page.
		/// </summary>
		/// <returns><c>true</c> if a page was loaded, <c>false</c> otherwise.</returns>
		private bool LoadExternallySelectedPage() {
			if(!string.IsNullOrEmpty(Request["Rollback"])) {
				externallySelectedPage = Pages.FindPage(Request["Rollback"]);
				if(externallySelectedPage != null) {
					AutoRollback();
					return true;
				}
			}

			if(!string.IsNullOrEmpty(Request["Admin"])) {
				externallySelectedPage = Pages.FindPage(Request["Admin"]);
				if(externallySelectedPage != null) {
					txtCurrentPage.Value = externallySelectedPage.FullName;
					ActivatePageEditor();
					return true;
				}
			}

			if(!string.IsNullOrEmpty(Request["Perms"])) {
				externallySelectedPage = Pages.FindPage(Request["Perms"]);
				if(externallySelectedPage != null) {
					txtCurrentPage.Value = externallySelectedPage.FullName;
					ActivatePagePermissionsManager();
					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Rolls back the externally selected page to the previous version.
		/// </summary>
		private void AutoRollback() {
			List<int> backups = Pages.GetBackups(externallySelectedPage);
			if(backups.Count > 0) {
				int targetRevision = backups[backups.Count - 1];

				Log.LogEntry("Page rollback requested for " + txtCurrentPage.Value + " to rev. " + targetRevision.ToString(), EntryType.General, SessionFacade.GetCurrentUsername());

				Pages.Rollback(externallySelectedPage, targetRevision);

				UrlTools.Redirect(externallySelectedPage.FullName + Settings.PageExtension);
			}
		}

		protected void btnNewPage_Click(object sender, EventArgs e) {
			// Redirect to the edit page, keeping the correct namespace
			string currentNamespace = lstNamespace.SelectedValue;
			if(!string.IsNullOrEmpty(currentNamespace)) currentNamespace += ".";
			Response.Redirect(currentNamespace + "Edit.aspx");
		}

		protected void pageSelector_SelectedPageChanged(object sender, SelectedPageChangedEventArgs e) {
			rangeBegin = e.SelectedPage * PageSize;
			rangeEnd = rangeBegin + e.ItemCount - 1;
			selectedPage = e.SelectedPage;

			rptPages.DataBind();
		}

		protected void lstNamespace_SelectedIndexChanged(object sender, EventArgs e) {
			currentPages = GetPages();
			pageSelector.ItemCount = currentPages.Count;
			pageSelector.SelectPage(0);

			rptPages.DataBind();

			string currentUser = SessionFacade.GetCurrentUsername();
			string[] currentGroups = SessionFacade.GetCurrentGroupNames();

			bool canManageAllPages = AuthChecker.CheckActionForNamespace(
				Pages.FindNamespace(lstNamespace.SelectedValue),
				Actions.ForNamespaces.ManagePages, currentUser, currentGroups);

			btnBulkMigrate.Enabled = canManageAllPages;
		}

		protected void btnFilter_Click(object sender, EventArgs e) {
			currentPages = GetPages();
			pageSelector.ItemCount = currentPages.Count;
			pageSelector.SelectPage(0);

			rptPages.DataBind();
		}

		protected void rdoFilter_CheckedChanged(object sender, EventArgs e) {
			currentPages = GetPages();
			pageSelector.ItemCount = currentPages.Count;
			pageSelector.SelectPage(0);

			rptPages.DataBind();
		}

		protected void rptPages_DataBinding(object sender, EventArgs e) {
			if(currentPages == null) currentPages = GetPages();
			NamespaceInfo nspace = DetectNamespaceInfo();

			List<PageRow> result = new List<PageRow>(PageSize);

			string currentUser = SessionFacade.GetCurrentUsername();
			string[] currentGroups = SessionFacade.GetCurrentGroupNames();

			bool canSetPermissions = AdminMaster.CanManagePermissions(currentUser, currentGroups);
			bool canDeletePages = AuthChecker.CheckActionForNamespace(nspace, Actions.ForNamespaces.DeletePages, currentUser, currentGroups);

			for(int i = rangeBegin; i <= rangeEnd; i++) {
				PageInfo page = currentPages[i];

				PageContent currentContent = Content.GetPageContent(page, false);

				// The page can be selected if the user can either manage or delete the page or manage the discussion
				// Repeat checks for enabling/disabling sections when a page is selected
				bool canEdit = AuthChecker.CheckActionForPage(page, Actions.ForPages.ModifyPage, currentUser, currentGroups);
				bool canManagePage = false;
				bool canManageDiscussion = false;
				if(!canDeletePages) canManagePage = AuthChecker.CheckActionForPage(page, Actions.ForPages.ManagePage, currentUser, currentGroups);
				if(!canDeletePages && !canManagePage) canManageDiscussion = AuthChecker.CheckActionForPage(page, Actions.ForPages.ManageDiscussion, currentUser, currentGroups);
				bool canSelect = canManagePage | canDeletePages | canManageDiscussion;

				int incomingLinks = Pages.GetPageIncomingLinks(page).Length;

				if(chkOrphansOnly.Checked && incomingLinks > 0) continue;

				PageContent firstContent = null;
				List<int> baks = Pages.GetBackups(page);
				if(baks.Count == 0) firstContent = currentContent;
				else firstContent = Pages.GetBackupContent(page, baks[0]);

				result.Add(new PageRow(page, currentContent, firstContent,
					Pages.GetMessageCount(page), baks.Count, incomingLinks == 0,
					canEdit, canSelect, canSetPermissions, txtCurrentPage.Value == page.FullName));
			}

			rptPages.DataSource = result;
		}

		protected void rptPages_ItemCommand(object sender, RepeaterCommandEventArgs e) {
			txtCurrentPage.Value = e.CommandArgument as string;

			if(e.CommandName == "Select") {
				// Permissions are checked in ActivatePageEditor()

				ActivatePageEditor();
			}
			else if(e.CommandName == "Perms") {
				if(!AdminMaster.CanManagePermissions(SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

				ActivatePagePermissionsManager();
			}
		}

		protected void btnPublic_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);

			RemoveAllPermissions(page);

			// Set permissions
			AuthWriter.SetPermissionForPage(AuthStatus.Grant, page, Actions.ForPages.ModifyPage,
				Users.FindUserGroup(Settings.AnonymousGroup));
			AuthWriter.SetPermissionForPage(AuthStatus.Grant, page, Actions.ForPages.PostDiscussion,
				Users.FindUserGroup(Settings.AnonymousGroup));

			RefreshPermissionsManager();
		}

		protected void btnAsNamespace_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);

			RemoveAllPermissions(page);

			RefreshPermissionsManager();
		}

		protected void btnLocked_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);

			RemoveAllPermissions(page);

			// Set permissions
			AuthWriter.SetPermissionForPage(AuthStatus.Deny, page, Actions.ForPages.ModifyPage,
				Users.FindUserGroup(Settings.UsersGroup));
			AuthWriter.SetPermissionForPage(AuthStatus.Deny, page, Actions.ForPages.ModifyPage,
				Users.FindUserGroup(Settings.AnonymousGroup));

			RefreshPermissionsManager();
		}

		/// <summary>
		/// Refreshes the permissions manager.
		/// </summary>
		private void RefreshPermissionsManager() {
			string r = permissionsManager.CurrentResourceName;
			permissionsManager.CurrentResourceName = r;
		}

		/// <summary>
		/// Removes all the permissions for a page.
		/// </summary>
		/// <param name="page">The page.</param>
		private void RemoveAllPermissions(PageInfo page) {
			AuthWriter.RemoveEntriesForPage(Users.FindUserGroup(Settings.AnonymousGroup), page);
			AuthWriter.RemoveEntriesForPage(Users.FindUserGroup(Settings.UsersGroup), page);
			AuthWriter.RemoveEntriesForPage(Users.FindUserGroup(Settings.AdministratorsGroup), page);
		}

		/// <summary>
		/// Populates the namespaces list for migration.
		/// </summary>
		/// <param name="page">The selected page.</param>
		/// <returns><c>true</c> if there is at least one valid target namespace, <c>false</c> otherwise.</returns>
		private bool PopulateTargetNamespaces(PageInfo page) {
			string currentUser = SessionFacade.GetCurrentUsername();
			string[] currentGroups = SessionFacade.GetCurrentGroupNames();

			lstTargetNamespace.Items.Clear();

			NamespaceInfo pageNamespace = Pages.FindNamespace(NameTools.GetNamespace(page.FullName));
			if(pageNamespace != null) {
				// Try adding Root as target namespace
				bool canManagePages = AuthChecker.CheckActionForNamespace(null, Actions.ForNamespaces.ManagePages, currentUser, currentGroups);
				if(canManagePages) lstTargetNamespace.Items.Add(new ListItem("<root>", ""));
			}

			// Try adding all other namespaces
			foreach(NamespaceInfo nspace in Pages.GetNamespaces().FindAll(n => n.Provider == page.Provider)) {
				if(pageNamespace == null || (pageNamespace != null && nspace.Name != pageNamespace.Name)) {
					bool canManagePages = AuthChecker.CheckActionForNamespace(nspace, Actions.ForNamespaces.ManagePages, currentUser, currentGroups);
					if(canManagePages) lstTargetNamespace.Items.Add(new ListItem(nspace.Name, nspace.Name));
				}
			}

			return lstTargetNamespace.Items.Count > 0;
		}

		/// <summary>
		/// Activates the page editor.
		/// </summary>
		private void ActivatePageEditor() {
			lblCurrentPage.Text = txtCurrentPage.Value;
			txtNewName.Text = NameTools.GetLocalName(txtCurrentPage.Value);

			// Enable/disable page sections
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			NamespaceInfo nspace = Pages.FindNamespace(NameTools.GetNamespace(page.FullName));
			string currentUser = SessionFacade.GetCurrentUsername();
			string[] currentGroups = SessionFacade.GetCurrentGroupNames();

			bool canApproveReject = AdminMaster.CanApproveDraft(page, currentUser, currentGroups);
			bool canDeletePages = AuthChecker.CheckActionForNamespace(nspace, Actions.ForNamespaces.DeletePages, currentUser, currentGroups);
			bool canManageAllPages = AuthChecker.CheckActionForNamespace(nspace, Actions.ForNamespaces.ManagePages, currentUser, currentGroups);
			bool canManagePage = AuthChecker.CheckActionForPage(page, Actions.ForPages.ManagePage, currentUser, currentGroups);
			bool canManageDiscussion = AuthChecker.CheckActionForPage(page, Actions.ForPages.ManageDiscussion, currentUser, currentGroups);
			bool namespaceAvailable = PopulateTargetNamespaces(page);

			// Approve/reject
			// Rename
			// Migrate
			// Rollback
			// Delete Backups
			// Clear discussion
			// Delete

			pnlApproveRevision.Enabled = canApproveReject;
			pnlRename.Enabled = canDeletePages;
			pnlMigrate.Enabled = canManageAllPages && namespaceAvailable;
			pnlRollback.Enabled = canManagePage;
			pnlDeleteBackups.Enabled = canManagePage;
			pnlClearDiscussion.Enabled = canManageDiscussion;
			pnlDelete.Enabled = canDeletePages;

			// Disable rename, migrate, delete for default page
			NamespaceInfo currentNamespace = Pages.FindNamespace(lstNamespace.SelectedValue);
			string currentDefaultPage = currentNamespace != null ? currentNamespace.DefaultPage.FullName : Settings.DefaultPage;
			if(txtCurrentPage.Value == currentDefaultPage) {
				btnRename.Enabled = false;
				btnMigrate.Enabled = false;
				btnDeletePage.Enabled = false;
			}

			LoadDraft(txtCurrentPage.Value);

			LoadBackups(txtCurrentPage.Value);

			btnRollback.Enabled = lstRevision.Items.Count > 0;
			btnDeleteBackups.Enabled = lstBackup.Items.Count > 0;

			pnlList.Visible = false;
			pnlEditPage.Visible = true;

			ClearResultLabels();
		}

		/// <summary>
		/// Activates the permissions manager.
		/// </summary>
		private void ActivatePagePermissionsManager() {
			lblPageName.Text = txtCurrentPage.Value;

			permissionsManager.CurrentResourceName = txtCurrentPage.Value;

			pnlList.Visible = false;
			pnlPermissions.Visible = true;
		}

		/// <summary>
		/// Loads the draft information, if any.
		/// </summary>
		/// <param name="currentPage">The current page name.</param>
		private void LoadDraft(string currentPage) {
			PageInfo page = Pages.FindPage(currentPage);
			PageContent draft = Pages.GetDraft(page);

			if(draft != null) {
				pnlApproveRevision.Visible = true;
				lblDateTime.Text = Preferences.AlignWithTimezone(draft.LastModified).ToString(Settings.DateTimeFormat);
				lblUser.Text = Users.UserLink(draft.User, true);

				// Ampersands are escaped automatically
				lnkEdit.NavigateUrl = "Edit.aspx?Page=" + Tools.UrlEncode(currentPage);
				lnkDiff.NavigateUrl = "Diff.aspx?Page=" + Tools.UrlEncode(currentPage) + "&Rev1=Current&Rev2=Draft";
				lblDraftPreview.Text = FormattingPipeline.FormatWithPhase3(
					FormattingPipeline.FormatWithPhase1And2(draft.Content, false, FormattingContext.PageContent, page), FormattingContext.PageContent, page);
			}
			else {
				pnlApproveRevision.Visible = false;
			}
		}

		/// <summary>
		/// Loads the backups for a page and populates the backups and revisions drop-down lists.
		/// </summary>
		/// <param name="pageName">The page.</param>
		private void LoadBackups(string pageName) {
			PageInfo page = Pages.FindPage(pageName);
			List<int> backups = Pages.GetBackups(page);

			lstRevision.Items.Clear();
			lstBackup.Items.Clear();

			foreach(int bak in backups) {
				PageContent bakContent = Pages.GetBackupContent(page, bak);
				
				ListItem item = new ListItem(bak.ToString() + ": " +
					Preferences.AlignWithTimezone(bakContent.LastModified).ToString(Settings.DateTimeFormat) + 
					" " + Properties.Messages.By + " " + bakContent.User,
					bak.ToString());

				// Add in reverse order - newer revisions at the top
				lstRevision.Items.Insert(0, item);
				lstBackup.Items.Insert(0, item);
			}
		}

		/// <summary>
		/// Clears all the result labels.
		/// </summary>
		private void ClearResultLabels() {
			lblApproveResult.CssClass = "";
			lblApproveResult.Text = "";
			lblRenameResult.CssClass = "";
			lblRenameResult.Text = "";
			lblRollbackResult.CssClass = "";
			lblRollbackResult.Text = "";
			lblBackupResult.CssClass = "";
			lblBackupResult.Text = "";
			lblDiscussionResult.CssClass = "";
			lblDiscussionResult.Text = "";
			lblDeleteResult.CssClass = "";
			lblDeleteResult.Text = "";
		}

		protected void rdoBackup_CheckedChanged(object sender, EventArgs e) {
			lstBackup.Enabled = rdoUpTo.Checked;
		}

		protected void btnBack_Click(object sender, EventArgs e) {
			// If page was specified in query string, don't refresh list
			ReturnToList();
			RefreshList();
		}

		/// <summary>
		/// Refreshes the pages list.
		/// </summary>
		private void RefreshList() {
			rangeBegin = pageSelector.SelectedPage * PageSize;
			rangeEnd = rangeBegin + pageSelector.SelectedPageSize - 1;
			selectedPage = pageSelector.SelectedPage;

			txtCurrentPage.Value = "";
			ResetEditor();
			rptPages.DataBind();
		}

		/// <summary>
		/// Returns to the group list.
		/// </summary>
		private void ReturnToList() {
			LoadExternallySelectedPage();
			if(externallySelectedPage != null) {
				// Return to page
				UrlTools.Redirect(externallySelectedPage.FullName + Settings.PageExtension);
			}
			else {
				// Return to list
				pnlEditPage.Visible = false;
				pnlPermissions.Visible = false;
				pnlBulkMigrate.Visible = false;
				pnlList.Visible = true;
			}
		}

		/// <summary>
		/// Resets the editor.
		/// </summary>
		private void ResetEditor() {
			btnRename.Enabled = true;
			btnMigrate.Enabled = true;
			btnDeletePage.Enabled = true;
			btnRollback.Enabled = true;
			btnDeleteBackups.Enabled = true;
			rdoAllBackups.Checked = false;
			rdoUpTo.Checked = false;
			lstBackup.Enabled = false;
		}

		protected void cvNewName_ServerValidate(object sender, ServerValidateEventArgs e) {
			e.IsValid = Pages.IsValidName(txtNewName.Text);
		}

		protected void btnApprove_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			if(!AdminMaster.CanApproveDraft(page, SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

			PageContent draft = Pages.GetDraft(page);

			Log.LogEntry("Page draft approval requested for " + draft.PageInfo.FullName, EntryType.General, SessionFacade.CurrentUsername);

			bool done = Pages.ModifyPage(draft.PageInfo, draft.Title, draft.User, draft.LastModified, draft.Comment,
				draft.Content, draft.Keywords, draft.Description, SaveMode.Backup);

			if(done) {
				Pages.DeleteDraft(draft.PageInfo);

				lblApproveResult.CssClass = "resultok";
				lblApproveResult.Text = Properties.Messages.DraftApproved;
				lblDraftPreview.Text = "";

				ReturnToList();
			}
			else {
				lblApproveResult.CssClass = "resulterror";
				lblApproveResult.Text = Properties.Messages.CouldNotApproveDraft;
			}
		}

		protected void btnReject_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			if(!AdminMaster.CanApproveDraft(page, SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

			Log.LogEntry("Page draft reject requested for " + page.FullName, EntryType.General, SessionFacade.CurrentUsername);

			Pages.DeleteDraft(page);

			lblApproveResult.CssClass = "resultok";
			lblApproveResult.Text = Properties.Messages.DraftRejected;
			lblDraftPreview.Text = "";

			ReturnToList();
		}

		protected void btnRename_Click(object sender, EventArgs e) {
			// Check name for change, validity and existence of another page with same name
			// Perform rename
			// Create shadow page, if needed

			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			if(!AuthChecker.CheckActionForNamespace(Pages.FindNamespace(NameTools.GetNamespace(page.FullName)), Actions.ForNamespaces.DeletePages,
				SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

			txtNewName.Text = txtNewName.Text.Trim();

			string currentNamespace = NameTools.GetNamespace(txtCurrentPage.Value);
			string currentPage = NameTools.GetLocalName(txtCurrentPage.Value);

			if(!Page.IsValid) return;

			if(txtNewName.Text.ToLowerInvariant() == currentPage.ToLowerInvariant()) {
				return;
			}

			if(Pages.FindPage(NameTools.GetFullName(currentNamespace, txtNewName.Text)) != null) {
				lblRenameResult.CssClass = "resulterror";
				lblRenameResult.Text = Properties.Messages.PageAlreadyExists;
				return;
			}

			Log.LogEntry("Page rename requested for " + txtCurrentPage.Value, EntryType.General, Log.SystemUsername);

			PageInfo oldPage = Pages.FindPage(txtCurrentPage.Value);
			PageContent oldContent = Content.GetPageContent(oldPage, false);

			bool done = Pages.RenamePage(oldPage, txtNewName.Text);

			if(done) {
				if(chkShadowPage.Checked) {
					done = Pages.CreatePage(currentNamespace, currentPage);

					if(done) {
						done = Pages.ModifyPage(Pages.FindPage(txtCurrentPage.Value),
							oldContent.Title, oldContent.User, oldContent.LastModified,
							oldContent.Comment, ">>> [" + txtNewName.Text + "]",
							new string[0], oldContent.Description, SaveMode.Normal);

						if(done) {
							ResetPageList();

							RefreshList();
							lblRenameResult.CssClass = "resultok";
							lblRenameResult.Text = Properties.Messages.PageRenamed;
							ReturnToList();
						}
						else {
							lblRenameResult.CssClass = "resulterror";
							lblRenameResult.Text = Properties.Messages.PageRenamedCouldNotSetShadowPageContent;
						}
					}
					else {
						lblRenameResult.CssClass = "resulterror";
						lblRenameResult.Text = Properties.Messages.PageRenamedCouldNotCreateShadowPage;
					}
				}
				else {
					RefreshList();
					lblRenameResult.CssClass = "resultok";
					lblRenameResult.Text = Properties.Messages.PageRenamed;
					ReturnToList();
				}
			}
			else {
				lblRenameResult.CssClass = "resulterror";
				lblRenameResult.Text = Properties.Messages.CouldNotRenamePage;
			}
		}

		protected void btnMigrate_Click(object sender, EventArgs e) {
			lblMigrateResult.CssClass = "";
			lblMigrateResult.Text = "";

			string currentUser = SessionFacade.GetCurrentUsername();
			string[] currentGroups = SessionFacade.GetCurrentGroupNames();

			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			NamespaceInfo targetNamespace = Pages.FindNamespace(lstTargetNamespace.SelectedValue);
			bool canManageAllPages = AuthChecker.CheckActionForNamespace(Pages.FindNamespace(NameTools.GetNamespace(page.FullName)),
				Actions.ForNamespaces.ManagePages, currentUser, currentGroups);
			bool canManageAllPagesInTarget = AuthChecker.CheckActionForNamespace(targetNamespace,
				Actions.ForNamespaces.ManagePages, currentUser, currentGroups);

			if(canManageAllPages && canManageAllPagesInTarget) {
				bool done = Pages.MigratePage(page, targetNamespace, chkCopyCategories.Checked);
				if(done) {
					chkCopyCategories.Checked = false;

					ResetPageList();

					RefreshList();
					lblRenameResult.CssClass = "resultok";
					lblRenameResult.Text = Properties.Messages.PageRenamed;
					ReturnToList();
				}
				else {
					lblMigrateResult.CssClass = "resulterror";
					lblMigrateResult.Text = Properties.Messages.CouldNotMigratePage;
				}
			}
		}

		protected void btnRollback_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			if(!AuthChecker.CheckActionForPage(page, Actions.ForPages.ManagePage, SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

			int targetRevision = -1;

			// This should never occur
			if(!int.TryParse(lstRevision.SelectedValue, out targetRevision)) return;

			Log.LogEntry("Page rollback requested for " + txtCurrentPage.Value + " to rev. " + targetRevision.ToString(), EntryType.General, Log.SystemUsername);

			bool done = Pages.Rollback(page, targetRevision);

			if(done) {
				RefreshList();
				lblRollbackResult.CssClass = "resultok";
				lblRollbackResult.Text = Properties.Messages.PageRolledBack;
				ReturnToList();
			}
			else {
				lblRollbackResult.CssClass = "resulterror";
				lblRollbackResult.Text = Properties.Messages.CouldNotRollbackPage;
			}
		}

		protected void btnDeleteBackups_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			if(!AuthChecker.CheckActionForPage(page, Actions.ForPages.ManagePage, SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

			int targetRevision = -1;

			// This should never occur
			if(!int.TryParse(lstBackup.SelectedValue, out targetRevision)) return;

			Log.LogEntry("Page backup deletion requested for " + txtCurrentPage.Value, EntryType.General, Log.SystemUsername);

			bool done = false;
			if(rdoAllBackups.Checked) {
				done = Pages.DeleteBackups(page);
			}
			else {
				done = Pages.DeleteBackups(page, targetRevision);
			}

			if(done) {
				RefreshList();
				lblBackupResult.CssClass = "resultok";
				lblBackupResult.Text = Properties.Messages.PageBackupsDeleted;
				ReturnToList();
			}
			else {
				lblBackupResult.CssClass = "resulterror";
				lblBackupResult.Text = Properties.Messages.CouldNotDeletePageBackups;
			}
		}

		protected void btnClearDiscussion_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			if(!AuthChecker.CheckActionForPage(page, Actions.ForPages.ManageDiscussion, SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

			Log.LogEntry("Page discussion cleanup requested for " + txtCurrentPage.Value, EntryType.General, Log.SystemUsername);

			bool done = Pages.RemoveAllMessages(page);

			if(done) {
				RefreshList();
				lblDiscussionResult.CssClass = "resultok";
				lblDiscussionResult.Text = Properties.Messages.AllMessagesDeleted;
				ReturnToList();
			}
			else {
				lblDiscussionResult.CssClass = "resulterror";
				lblDiscussionResult.Text = Properties.Messages.CouldNotDeleteOneOrMoreMessages;
			}
		}

		protected void btnDeletePage_Click(object sender, EventArgs e) {
			PageInfo page = Pages.FindPage(txtCurrentPage.Value);
			if(!AuthChecker.CheckActionForNamespace(Pages.FindNamespace(NameTools.GetNamespace(page.FullName)), Actions.ForNamespaces.DeletePages,
				SessionFacade.GetCurrentUsername(), SessionFacade.GetCurrentGroupNames())) return;

			Log.LogEntry("Page deletion requested for " + txtCurrentPage.Value, EntryType.General, Log.SystemUsername);

			bool done = Pages.DeletePage(page);

			if(done) {
				ResetPageList();

				RefreshList();
				lblDeleteResult.CssClass = "resultok";
				lblDeleteResult.Text = Properties.Messages.PageDeleted;
				ReturnToList();
			}
			else {
				lblDeleteResult.CssClass = "resulterror";
				lblDeleteResult.Text = Properties.Messages.CouldNotDeletePage;
			}
		}

		/// <summary>
		/// Resets the bulk management editor.
		/// </summary>
		private void ResetBulkEditor() {
			pageListBuilder.ResetControl();

			chkBulkMigrateCopyCategories.Checked = false;

			lblBulkMigrateResult.CssClass = "";
			lblBulkMigrateResult.Text = "";
		}

		/// <summary>
		/// Loads target namespaces for bulk migration.
		/// </summary>
		private void LoadTargetNamespaces() {
			// Load valid namespaces, filtering the current one
			lstBulkMigrateTargetNamespace.Items.Clear();

			bool canManageAllPages = false;
			string currentUser = SessionFacade.GetCurrentUsername();
			string[] currentGroups = SessionFacade.GetCurrentGroupNames();

			if(!string.IsNullOrEmpty(lstNamespace.SelectedValue)) {
				// Root namespace
				canManageAllPages = AuthChecker.CheckActionForNamespace(null,
					Actions.ForNamespaces.ManagePages, currentUser, currentGroups);

				if(canManageAllPages) {
					lstBulkMigrateTargetNamespace.Items.Add(new ListItem("<root>", "."));
				}
			}

			foreach(NamespaceInfo ns in Pages.GetNamespaces().FindAll(n => n.Provider.GetType().FullName == providerSelector.SelectedProvider)) {
				// All sub-namespaces
				if(ns.Name != lstNamespace.SelectedValue) {
					canManageAllPages = AuthChecker.CheckActionForNamespace(ns,
						Actions.ForNamespaces.ManagePages, currentUser, currentGroups);

					if(canManageAllPages) {
						lstBulkMigrateTargetNamespace.Items.Add(new ListItem(ns.Name, ns.Name));
					}
				}
			}
		}

		protected void providerSelector_SelectedProviderChanged(object sender, EventArgs e) {
			pageListBuilder.CurrentProvider = providerSelector.SelectedProvider;
			LoadTargetNamespaces();
		}

		protected void btnBulkMigrateBack_Click(object sender, EventArgs e) {
			RefreshList();
			ReturnToList();
		}

		protected void btnBulkMigrate_Click(object sender, EventArgs e) {
			ResetBulkEditor();
			pageListBuilder.CurrentNamespace = lstNamespace.SelectedValue;
			pageListBuilder.CurrentProvider = providerSelector.SelectedProvider;
			pnlBulkMigrate.Visible = true;
			pnlList.Visible = false;

			LoadTargetNamespaces();

			btnBulkMigratePages.Enabled = lstBulkMigrateTargetNamespace.Items.Count > 0;
		}

		/// <summary>
		/// Determines whether a page is the default page of its namespace.
		/// </summary>
		/// <param name="page">The page.</param>
		/// <returns><c>true</c> if the page is the default namespace of its namespace, <c>false</c> otherwise.</returns>
		private bool IsDefaultPage(PageInfo page) {
			string localName, nspace;
			NameTools.ExpandFullName(page.FullName, out nspace, out localName);

			if(string.IsNullOrEmpty(nspace)) {
				return localName.ToLowerInvariant() == Settings.DefaultPage.ToLowerInvariant();
			}
			else {
				NamespaceInfo ns = Pages.FindNamespace(nspace);
				return ns.DefaultPage != null && ns.DefaultPage.FullName.ToLowerInvariant() == page.FullName.ToLowerInvariant();
			}
		}

		protected void btnBulkMigratePages_Click(object sender, EventArgs e) {
			lblBulkMigrateResult.CssClass = "";
			lblBulkMigrateResult.Text = "";

			List<PageInfo> selectedPages = new List<PageInfo>(20);
			foreach(string pg in pageListBuilder.SelectedPages) {
				PageInfo page = Pages.FindPage(pg);
				if(page != null && !IsDefaultPage(page)) selectedPages.Add(page);
			}

			if(selectedPages.Count == 0) {
				lblBulkMigrateResult.CssClass = "resulterror";
				lblBulkMigrateResult.Text = Properties.Messages.NoPagesToMigrate;
				return;
			}

			string nspaceName = lstBulkMigrateTargetNamespace.SelectedValue;
			if(nspaceName == ".") nspaceName = null;
			NamespaceInfo selectedNamespace = Pages.FindNamespace(nspaceName);

			Log.LogEntry("Bulk migration requested", EntryType.General, SessionFacade.CurrentUsername);

			bool allDone = true;
			foreach(PageInfo pg in selectedPages) {
				allDone &= Pages.MigratePage(pg, selectedNamespace, chkBulkMigrateCopyCategories.Checked);
			}

			if(allDone) {
				lblBulkMigrateResult.CssClass = "resultok";
				lblBulkMigrateResult.Text = Properties.Messages.BulkMigrationCompleted;
			}
			else {
				lblBulkMigrateResult.CssClass = "resulterror";
				lblBulkMigrateResult.Text = Properties.Messages.BulkMigrationCompletedWithErrors;
			}

			ResetPageList();
			rptPages.DataBind();

			pageListBuilder.ResetControl();
		}

	}

	/// <summary>
	/// Represents a Page for display purposes.
	/// </summary>
	public class PageRow {

		private string fullName, title, createdBy, createdOn, lastModifiedBy, lastModifiedOn, discussion, revisions, provider, additionalClass;
		private bool isOrphan, canEdit, canSelect, canSetPermissions;

		/// <summary>
		/// Initializes a new instance of the <see cref="T:PageRow" /> class.
		/// </summary>
		/// <param name="page">The original page.</param>
		/// <param name="currentContent">The current content.</param>
		/// <param name="firstContent">The first revision content.</param>
		/// <param name="discussionCount">The number of messages in the discussion.</param>
		/// <param name="revisionCount">The number of revisions.</param>
		/// <param name="isOrphan">A value indicating whether the page is orphan.</param>
		/// <param name="canEdit">A value indicating whether the current user can edit the page.</param>
		/// <param name="canSelect">A value indicating whether the current user can select the page.</param>
		/// <param name="canSetPermissions">A value indicating whether the current user can set permissions for the page.</param>
		/// <param name="selected">A value indicating whether the page is selected.</param>
		public PageRow(PageInfo page, PageContent currentContent, PageContent firstContent, int discussionCount, int revisionCount,
			bool isOrphan, bool canEdit, bool canSelect, bool canSetPermissions, bool selected) {

			fullName = page.FullName;
			title = FormattingPipeline.PrepareTitle(currentContent.Title, false, FormattingContext.Other, page);
			createdBy = firstContent.User;
			createdOn = Preferences.AlignWithTimezone(page.CreationDateTime).ToString(Settings.DateTimeFormat);
			lastModifiedBy = currentContent.User;
			lastModifiedOn = Preferences.AlignWithTimezone(currentContent.LastModified).ToString(Settings.DateTimeFormat);
			discussion = discussionCount.ToString();
			revisions = revisionCount.ToString();
			provider = page.Provider.Information.Name;

			this.isOrphan = isOrphan;

			this.canEdit = canEdit;
			this.canSelect = canSelect;
			this.canSetPermissions = canSetPermissions;
			additionalClass = selected ? " selected" : "";
		}

		/// <summary>
		/// Gets the full name.
		/// </summary>
		public string FullName {
			get { return fullName; }
		}

		/// <summary>
		/// Gets the title.
		/// </summary>
		public string Title {
			get { return title; }
		}

		/// <summary>
		/// Gets the original page creator.
		/// </summary>
		public string CreatedBy {
			get { return createdBy; }
		}

		/// <summary>
		/// Gets the original creation date/time.
		/// </summary>
		public string CreatedOn {
			get { return createdOn; }
		}

		/// <summary>
		/// Gets the user who last modified the page.
		/// </summary>
		public string LastModifiedBy {
			get { return lastModifiedBy; }
		}

		/// <summary>
		/// Gets the last modification date/time.
		/// </summary>
		public string LastModifiedOn {
			get { return lastModifiedOn; }
		}

		/// <summary>
		/// Gets the discussion message count.
		/// </summary>
		public string Discussion {
			get { return discussion; }
		}

		/// <summary>
		/// Gets the number of revisions.
		/// </summary>
		public string Revisions {
			get { return revisions; }
		}

		/// <summary>
		/// Gets the provider.
		/// </summary>
		public string Provider {
			get { return provider; }
		}

		/// <summary>
		/// Gets a value indicating whether the page is orhpan.
		/// </summary>
		public bool IsOrphan {
			get { return isOrphan; }
		}

		/// <summary>
		/// Gets a value indicating whether the current user can edit the page.
		/// </summary>
		public bool CanEdit {
			get { return canEdit; }
		}

		/// <summary>
		/// Gets a value indicating whether the current user can select the page.
		/// </summary>
		public bool CanSelect {
			get { return canSelect; }
		}

		/// <summary>
		/// Gets a value indicating whether the current user can set permissions for the page.
		/// </summary>
		public bool CanSetPermissions {
			get { return canSetPermissions; }
		}

		/// <summary>
		/// Gets the additional CSS class.
		/// </summary>
		public string AdditionalClass {
			get { return additionalClass; }
		}

	}

}