using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ScrewTurn.Wiki.PluginFramework;
using ScrewTurn.Wiki.SearchEngine;
namespace ScrewTurn.Wiki.Plugins.PluginPack {
///
/// Implements a sandbox plugin for pages.
///
public class PagesSandbox {
private IHostV30 host;
private string config;
private static readonly ComponentInformation info = new ComponentInformation("Pages Sandbox", "ScrewTurn Software", "3.0.0.180", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/PagesSandbox.txt");
private List allNamespaces = new List(5);
private List allPages;
private Dictionary allContents;
private Dictionary> allBackups;
private Dictionary allDrafts;
private List allCategories;
private uint freeDocumentId = 1;
private uint freeWordId = 1;
private IInMemoryIndex index;
///
/// Gets a namespace.
///
/// The name of the namespace (cannot be null or empty).
/// The , or null if no namespace is found.
public NamespaceInfo GetNamespace(string name) {
if(name == null) throw new ArgumentNullException("name");
if(name.Length == 0) throw new ArgumentException("Name cannot be empty");
return allNamespaces.Find(n => n.Name == name);
}
///
/// Gets all the sub-namespaces.
///
/// The sub-namespaces, sorted by name.
public NamespaceInfo[] GetNamespaces() {
lock(this) {
return allNamespaces.ToArray();
}
}
///
/// Adds a new namespace.
///
/// The name of the namespace.
/// The correct object.
public NamespaceInfo AddNamespace(string name) {
throw new NotImplementedException();
/*if(name == null) throw new ArgumentNullException("name");
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
lock(this) {
if(GetNamespace(name) != null) return null;
// This does not compile unless PagesSandbox implements IPagesStorageProviderV30
NamespaceInfo newSpace = new NamespaceInfo(name, this, null);
allNamespaces.Add(newSpace);
return newSpace;
}*/
}
///
/// Renames a namespace.
///
/// The namespace to rename.
/// The new name of the namespace.
/// The correct object.
public NamespaceInfo RenameNamespace(NamespaceInfo nspace, string newName) {
if(nspace == null) throw new ArgumentNullException("nspace");
if(newName == null) throw new ArgumentNullException("newName");
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
lock(this) {
if(GetNamespace(newName) != null) return null;
nspace.Name = newName;
return nspace;
}
}
///
/// Sets the default page of a namespace.
///
/// The namespace of which to set the default page.
/// The page to use as default page, or null.
/// The correct object.
public NamespaceInfo SetNamespaceDefaultPage(NamespaceInfo nspace, PageInfo page) {
if(nspace == null) throw new ArgumentNullException("nspace");
lock(this) {
nspace.DefaultPage = page;
return nspace;
}
}
///
/// Removes a namespace.
///
/// The namespace to remove.
/// true if the namespace is removed, false otherwise.
public bool RemoveNamespace(NamespaceInfo nspace) {
if(nspace == null) throw new ArgumentNullException("nspace");
lock(this) {
return allNamespaces.Remove(nspace);
}
}
///
/// Moves a page from its namespace into another.
///
/// The page to move.
/// The destination namespace (null for the root).
/// A value indicating whether to copy the page categories in the destination
/// namespace, if not already available.
/// The correct instance of .
public PageInfo MovePage(PageInfo page, NamespaceInfo destination, bool copyCategories) {
throw new NotImplementedException();
}
///
/// Gets a category.
///
/// The full name of the category.
/// The , or null if no category is found.
public CategoryInfo GetCategory(string fullName) {
throw new NotImplementedException();
}
///
/// Gets all the Categories in a namespace.
///
/// The namespace.
/// All the Categories in the namespace, sorted by name.
public CategoryInfo[] GetCategories(NamespaceInfo nspace) {
throw new NotImplementedException();
}
///
/// Gets all the categories of a page.
///
/// The page.
/// The categories, sorted by name.
public CategoryInfo[] GetCategoriesForPage(PageInfo page) {
throw new NotImplementedException();
}
///
/// Adds a Category.
///
/// The target namespace (null for the root).
/// The Category name.
/// The correct CategoryInfo object.
/// The moethod should set category's Pages to an empty array.
public CategoryInfo AddCategory(string nspace, string name) {
throw new NotImplementedException();
}
///
/// Renames a Category.
///
/// The Category to rename.
/// The new Name.
/// The correct CategoryInfo object.
public CategoryInfo RenameCategory(CategoryInfo category, string newName) {
throw new NotImplementedException();
}
///
/// Removes a Category.
///
/// The Category to remove.
/// True if the Category has been removed successfully.
public bool RemoveCategory(CategoryInfo category) {
throw new NotImplementedException();
}
///
/// Merges two Categories.
///
/// The source Category.
/// The destination Category.
/// The correct object.
/// The destination Category remains, while the source Category is deleted, and all its Pages re-bound
/// in the destination Category.
public CategoryInfo MergeCategories(CategoryInfo source, CategoryInfo destination) {
throw new NotImplementedException();
}
///
/// Performs a search in the index.
///
/// The search parameters.
/// The results.
public SearchResultCollection PerformSearch(SearchParameters parameters) {
throw new NotImplementedException();
}
///
/// Rebuilds the search index.
///
public void RebuildIndex() {
throw new NotImplementedException();
}
///
/// Gets some statistics about the search engine index.
///
/// The total number of documents.
/// The total number of unique words.
/// The total number of word-document occurrences.
/// The approximated size, in bytes, of the search engine index.
public void GetIndexStats(out int documentCount, out int wordCount, out int occurrenceCount, out long size) {
throw new NotImplementedException();
}
///
/// Gets a value indicating whether the search engine index is corrupted and needs to be rebuilt.
///
public bool IsIndexCorrupted {
get { throw new NotImplementedException(); }
}
///
/// Gets a page.
///
/// The full name of the page.
/// The , or null if no page is found.
public PageInfo GetPage(string fullName) {
throw new NotImplementedException();
}
///
/// Gets all the Pages in a namespace.
///
/// The namespace (null for the root).
/// All the Pages in the namespace, sorted by name.
public PageInfo[] GetPages(NamespaceInfo nspace) {
throw new NotImplementedException();
}
///
/// Gets all the pages in a namespace that are bound to zero categories.
///
/// The namespace (null for the root).
/// The pages, sorted by name.
public PageInfo[] GetUncategorizedPages(NamespaceInfo nspace) {
throw new NotImplementedException();
}
///
/// Gets the Content of a Page.
///
/// The Page.
/// The Page Content object.
public PageContent GetContent(PageInfo page) {
throw new NotImplementedException();
}
///
/// Gets the content of a draft of a Page.
///
/// The Page.
/// The draft, or null if no draft exists.
public PageContent GetDraft(PageInfo page) {
throw new NotImplementedException();
}
///
/// Deletes a draft of a Page.
///
/// The page.
/// true if the draft is deleted, false otherwise.
public bool DeleteDraft(PageInfo page) {
throw new NotImplementedException();
}
///
/// Gets the Backup/Revision numbers of a Page.
///
/// The Page to get the Backups of.
/// The Backup/Revision numbers.
public int[] GetBackups(PageInfo page) {
throw new NotImplementedException();
}
///
/// Gets the Content of a Backup of a Page.
///
/// The Page to get the backup of.
/// The Backup/Revision number.
/// The Page Backup.
public PageContent GetBackupContent(PageInfo page, int revision) {
throw new NotImplementedException();
}
///
/// Forces to overwrite or create a Backup.
///
/// The Backup content.
/// The revision.
/// True if the Backup has been created successfully.
public bool SetBackupContent(PageContent content, int revision) {
throw new NotImplementedException();
}
///
/// Adds a Page.
///
/// The target namespace (null for the root).
/// The Page Name.
/// The creation Date/Time.
/// The correct PageInfo object or null.
/// This method should not create the content of the Page.
public PageInfo AddPage(string nspace, string name, DateTime creationDateTime) {
throw new NotImplementedException();
}
///
/// Renames a Page.
///
/// The Page to rename.
/// The new Name.
/// The correct object.
public PageInfo RenamePage(PageInfo page, string newName) {
throw new NotImplementedException();
}
///
/// Modifies the Content of a Page.
///
/// The Page.
/// The Title of the Page.
/// The Username.
/// The Date/Time.
/// The Comment of the editor, about this revision.
/// The Page Content.
/// The keywords, usually used for SEO.
/// The description, usually used for SEO.
/// The save mode for this modification.
/// true if the Page has been modified successfully, false otherwise.
/// If saveMode equals Draft and a draft already exists, it is overwritten.
public bool ModifyPage(PageInfo page, string title, string username, DateTime dateTime, string comment, string content, string[] keywords, string description, SaveMode saveMode) {
throw new NotImplementedException();
}
///
/// Performs the rollback of a Page to a specified revision.
///
/// The Page to rollback.
/// The Revision to rollback the Page to.
/// true if the rollback succeeded, false otherwise.
public bool RollbackPage(PageInfo page, int revision) {
throw new NotImplementedException();
}
///
/// Deletes the Backups of a Page, up to a specified revision.
///
/// The Page to delete the backups of.
/// The newest revision to delete (newer revision are kept) or -1 to delete all the Backups.
/// true if the deletion succeeded, false otherwise.
public bool DeleteBackups(PageInfo page, int revision) {
throw new NotImplementedException();
}
///
/// Removes a Page.
///
/// The Page to remove.
/// True if the Page is removed successfully.
public bool RemovePage(PageInfo page) {
throw new NotImplementedException();
}
///
/// Binds a Page with one or more Categories.
///
/// The Page to bind.
/// The Categories to bind the Page with.
/// True if the binding succeeded.
/// After a successful operation, the Page is bound with all and only the categories passed as argument.
public bool RebindPage(PageInfo page, string[] categories) {
throw new NotImplementedException();
}
///
/// Gets the Page Messages.
///
/// The Page.
/// The list of the first-level Messages, containing the replies properly nested, sorted by date/time.
public Message[] GetMessages(PageInfo page) {
throw new NotImplementedException();
}
///
/// Gets the total number of Messages in a Page Discussion.
///
/// The Page.
/// The number of messages.
public int GetMessageCount(PageInfo page) {
throw new NotImplementedException();
}
///
/// Removes all messages for a page and stores the new messages.
///
/// The page.
/// The new messages to store.
/// true if the messages are stored, false otherwise.
public bool BulkStoreMessages(PageInfo page, Message[] messages) {
throw new NotImplementedException();
}
///
/// Adds a new Message to a Page.
///
/// The Page.
/// The Username.
/// The Subject.
/// The Date/Time.
/// The Body.
/// The Parent Message ID, or -1.
/// True if the Message is added successfully.
public bool AddMessage(PageInfo page, string username, string subject, DateTime dateTime, string body, int parent) {
throw new NotImplementedException();
}
///
/// Removes a Message.
///
/// The Page.
/// The ID of the Message to remove.
/// A value specifying whether or not to remove the replies.
/// True if the Message is removed successfully.
public bool RemoveMessage(PageInfo page, int id, bool removeReplies) {
throw new NotImplementedException();
}
///
/// Modifies a Message.
///
/// The Page.
/// The ID of the Message to modify.
/// The Username.
/// The Subject.
/// The Date/Time.
/// The Body.
/// True if the Message is modified successfully.
public bool ModifyMessage(PageInfo page, int id, string username, string subject, DateTime dateTime, string body) {
throw new NotImplementedException();
}
///
/// Gets all the Navigation Paths in a Namespace.
///
/// The Namespace.
/// All the Navigation Paths, sorted by name.
public NavigationPath[] GetNavigationPaths(NamespaceInfo nspace) {
throw new NotImplementedException();
}
///
/// Adds a new Navigation Path.
///
/// The target namespace (null for the root).
/// The Name of the Path.
/// The Pages array.
/// The correct object.
public NavigationPath AddNavigationPath(string nspace, string name, PageInfo[] pages) {
throw new NotImplementedException();
}
///
/// Modifies an existing navigation path.
///
/// The navigation path to modify.
/// The new pages array.
/// The correct object.
public NavigationPath ModifyNavigationPath(NavigationPath path, PageInfo[] pages) {
throw new NotImplementedException();
}
///
/// Removes a Navigation Path.
///
/// The navigation path to remove.
/// true if the path is removed, false otherwise.
public bool RemoveNavigationPath(NavigationPath path) {
throw new NotImplementedException();
}
///
/// Gets all the snippets.
///
/// All the snippets, sorted by name.
public Snippet[] GetSnippets() {
throw new NotImplementedException();
}
///
/// Adds a new snippet.
///
/// The name of the snippet.
/// The content of the snippet.
/// The correct object.
public Snippet AddSnippet(string name, string content) {
throw new NotImplementedException();
}
///
/// Modifies an existing snippet.
///
/// The name of the snippet to modify.
/// The content of the snippet.
/// The correct object.
public Snippet ModifySnippet(string name, string content) {
throw new NotImplementedException();
}
///
/// Removes a new Snippet.
///
/// The Name of the Snippet to remove.
/// true if the snippet is removed, false otherwise.
public bool RemoveSnippet(string name) {
throw new NotImplementedException();
}
///
/// Gets all the content templates.
///
/// All the content templates, sorted by name.
public ContentTemplate[] GetContentTemplates() {
throw new NotImplementedException();
}
///
/// Adds a new content template.
///
/// The name of template.
/// The content of the template.
/// The correct object.
public ContentTemplate AddContentTemplate(string name, string content) {
throw new NotImplementedException();
}
///
/// Modifies an existing content template.
///
/// The name of the template to modify.
/// The content of the template.
/// The correct object.
public ContentTemplate ModifyContentTemplate(string name, string content) {
throw new NotImplementedException();
}
///
/// Removes a content template.
///
/// The name of the template to remove.
/// true if the template is removed, false otherwise.
public bool RemoveContentTemplate(string name) {
throw new NotImplementedException();
}
///
/// Gets a value specifying whether the provider is read-only, i.e. it can only provide data and not store it.
///
public bool ReadOnly {
get { throw new NotImplementedException(); }
}
///
/// Initializes the Storage Provider.
///
/// The Host of the Component.
/// The Configuration data, if any.
/// If the configuration string is not valid, the methoud should throw a .
public void Init(IHostV30 host, string config) {
if(host == null) throw new ArgumentNullException("host");
if(config == null) throw new ArgumentNullException("config");
this.host = host;
this.config = config;
allPages = new List(50);
allContents = new Dictionary(50);
allBackups = new Dictionary>(50);
allDrafts = new Dictionary(5);
allCategories = new List(10);
// Prepare search index
index = new StandardIndex();
index.SetBuildDocumentDelegate(BuildDocumentHandler);
index.IndexChanged += index_IndexChanged;
}
private void index_IndexChanged(object sender, IndexChangedEventArgs e) {
lock(this) {
if(e.Change == IndexChangeType.DocumentAdded) {
List newWords = new List(e.ChangeData.Words.Count);
foreach(DumpedWord w in e.ChangeData.Words) {
newWords.Add(new WordId(w.Text, freeWordId));
freeWordId++;
}
e.Result = new IndexStorerResult(freeDocumentId, newWords);
freeDocumentId++;
}
else e.Result = null;
}
}
///
/// Handles the construction of an for the search engine.
///
/// The input dumped document.
/// The resulting .
private IDocument BuildDocumentHandler(DumpedDocument dumpedDocument) {
if(dumpedDocument.TypeTag == PageDocument.StandardTypeTag) {
string pageName = PageDocument.GetPageName(dumpedDocument.Name);
PageInfo page = GetPage(pageName);
if(page == null) return null;
else return new PageDocument(page, dumpedDocument, TokenizeContent);
}
else if(dumpedDocument.TypeTag == MessageDocument.StandardTypeTag) {
string pageFullName;
int id;
MessageDocument.GetMessageDetails(dumpedDocument.Name, out pageFullName, out id);
PageInfo page = GetPage(pageFullName);
if(page == null) return null;
else return new MessageDocument(page, id, dumpedDocument, TokenizeContent);
}
else return null;
}
///
/// Tokenizes page content.
///
/// The content to tokenize.
/// The tokenized words.
private static WordInfo[] TokenizeContent(string content) {
WordInfo[] words = SearchEngine.Tools.Tokenize(content);
return words;
}
///
/// Indexes a page.
///
/// The content of the page.
/// The number of indexed words, including duplicates.
private int IndexPage(PageContent content) {
lock(this) {
string documentName = PageDocument.GetDocumentName(content.PageInfo);
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(content.PageInfo, content.Title),
PageDocument.StandardTypeTag, content.LastModified);
// Store the document
// The content should always be prepared using IHost.PrepareForSearchEngineIndexing()
return index.StoreDocument(new PageDocument(content.PageInfo, ddoc, TokenizeContent),
content.Keywords, host.PrepareContentForIndexing(content.PageInfo, content.Content), null);
}
}
///
/// Removes a page from the search engine index.
///
/// The content of the page to remove.
private void UnindexPage(PageContent content) {
lock(this) {
string documentName = PageDocument.GetDocumentName(content.PageInfo);
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(content.PageInfo, content.Title),
PageDocument.StandardTypeTag, content.LastModified);
index.RemoveDocument(new PageDocument(content.PageInfo, ddoc, TokenizeContent), null);
}
}
///
/// Indexes a message.
///
/// The page.
/// The message ID.
/// The subject.
/// The date/time.
/// The body.
/// The number of indexed words, including duplicates.
private int IndexMessage(PageInfo page, int id, string subject, DateTime dateTime, string body) {
lock(this) {
// Trim "RE:" to avoid polluting the search engine index
if(subject.ToLowerInvariant().StartsWith("re:") && subject.Length > 3) subject = subject.Substring(3).Trim();
string documentName = MessageDocument.GetDocumentName(page, id);
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(null, subject),
MessageDocument.StandardTypeTag, dateTime);
// Store the document
// The content should always be prepared using IHost.PrepareForSearchEngineIndexing()
return index.StoreDocument(new MessageDocument(page, id, ddoc, TokenizeContent), null,
host.PrepareContentForIndexing(null, body), null);
}
}
///
/// Indexes a message tree.
///
/// The page.
/// The tree root.
private void IndexMessageTree(PageInfo page, Message root) {
IndexMessage(page, root.ID, root.Subject, root.DateTime, root.Body);
foreach(Message reply in root.Replies) {
IndexMessageTree(page, reply);
}
}
///
/// Removes a message from the search engine index.
///
/// The page.
/// The message ID.
/// The subject.
/// The date/time.
/// The body.
/// The number of indexed words, including duplicates.
private void UnindexMessage(PageInfo page, int id, string subject, DateTime dateTime, string body) {
lock(this) {
// Trim "RE:" to avoid polluting the search engine index
if(subject.ToLowerInvariant().StartsWith("re:") && subject.Length > 3) subject = subject.Substring(3).Trim();
string documentName = MessageDocument.GetDocumentName(page, id);
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(null, subject),
MessageDocument.StandardTypeTag, DateTime.Now);
index.RemoveDocument(new MessageDocument(page, id, ddoc, TokenizeContent), null);
}
}
///
/// Removes a message tree from the search engine index.
///
/// The page.
/// The tree root.
private void UnindexMessageTree(PageInfo page, Message root) {
UnindexMessage(page, root.ID, root.Subject, root.DateTime, root.Body);
foreach(Message reply in root.Replies) {
UnindexMessageTree(page, reply);
}
}
///
/// Method invoked on shutdown.
///
/// This method might not be invoked in some cases.
public void Shutdown() {
// Nothing do to
}
///
/// Gets the Information about the Provider.
///
public ComponentInformation Information {
get { return info; }
}
///
/// Gets a brief summary of the configuration string format, in HTML. Returns null if no configuration is needed.
///
public string ConfigHelpHtml {
get { return null; }
}
}
}