using System; using System.Collections.Generic; using System.IO; using System.Text; using ScrewTurn.Wiki.PluginFramework; namespace ScrewTurn.Wiki { /// /// Implements a Local Files Storage Provider. /// public class FilesStorageProvider : IFilesStorageProviderV30 { private readonly ComponentInformation info = new ComponentInformation("Local Files Provider", "ScrewTurn Software", Settings.WikiVersion, "http://www.screwturn.eu", null); // The following strings MUST terminate with DirectorySeparatorPath in order to properly work // in BuildFullPath method private readonly string UploadDirectory = "Upload" + Path.DirectorySeparatorChar; private readonly string AttachmentsDirectory = "Attachments" + Path.DirectorySeparatorChar; private const string FileDownloadsFile = "FileDownloads.cs"; private const string AttachmentDownloadsFile = "AttachmentDownloads.cs"; // 16 KB buffer used in the StreamCopy method // 16 KB seems to be the best break-even between performance and memory usage private const int BufferSize = 16384; private IHostV30 host; private string GetFullPath(string finalChunk) { return Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), finalChunk); } /// /// Initializes the Storage Provider. /// /// The Host of the Component. /// The Configuration data, if any. /// If or are null. /// If is not valid or is incorrect. public void Init(IHostV30 host, string config) { if(host == null) throw new ArgumentNullException("host"); if(config == null) throw new ArgumentNullException("config"); this.host = host; if(!LocalProvidersTools.CheckWritePermissions(host.GetSettingValue(SettingName.PublicDirectory))) { throw new InvalidConfigurationException("Cannot write into the public directory - check permissions"); } // Create directories, if needed if(!Directory.Exists(GetFullPath(UploadDirectory))) { Directory.CreateDirectory(GetFullPath(UploadDirectory)); } if(!Directory.Exists(GetFullPath(AttachmentsDirectory))) { Directory.CreateDirectory(GetFullPath(AttachmentsDirectory)); } if(!File.Exists(GetFullPath(FileDownloadsFile))) { File.Create(GetFullPath(FileDownloadsFile)).Close(); } if(!File.Exists(GetFullPath(AttachmentDownloadsFile))) { File.Create(GetFullPath(AttachmentDownloadsFile)).Close(); } } /// /// Method invoked on shutdown. /// /// This method might not be invoked in some cases. public void Shutdown() { // Nothing to do } /// /// 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; } } /// /// 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 { return false; } } /// /// Builds a full path from a provider-specific partial path. /// /// The partial path. /// The full path. /// For example: if partialPath is "/my/directory", the method returns /// "C:\Inetpub\wwwroot\Wiki\public\Upload\my\directory", assuming the Wiki resides in "C:\Inetpub\wwwroot\Wiki". private string BuildFullPath(string partialPath) { if(partialPath == null) partialPath = ""; partialPath = partialPath.Replace("/", Path.DirectorySeparatorChar.ToString()).TrimStart(Path.DirectorySeparatorChar); string up = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), UploadDirectory); return Path.Combine(up, partialPath); // partialPath CANNOT start with "\" -> Path.Combine does not work } /// /// Builds a full path from a provider-specific partial path. /// /// The partial path. /// The full path. /// For example: if partialPath is "/my/directory", the method returns /// "C:\Inetpub\wwwroot\Wiki\public\Attachments\my\directory", assuming the Wiki resides in "C:\Inetpub\wwwroot\Wiki". private string BuildFullPathForAttachments(string partialPath) { if(partialPath == null) partialPath = ""; partialPath = partialPath.Replace("/", Path.DirectorySeparatorChar.ToString()).TrimStart(Path.DirectorySeparatorChar); string up = Path.Combine(host.GetSettingValue(SettingName.PublicDirectory), AttachmentsDirectory); return Path.Combine(up, partialPath); // partialPath CANNOT start with "\" -> Path.Combine does not work } /// /// Lists the Files in the specified Directory. /// /// The full directory name, for example "/my/directory". Null, empty or "/" for the root directory. /// The list of Files in the directory. /// If does not exist. public string[] ListFiles(string directory) { string d = BuildFullPath(directory); if(!Directory.Exists(d)) throw new ArgumentException("Directory does not exist", "directory"); string[] temp = Directory.GetFiles(d); // Result must be transformed in the form /my/dir/file.ext List res = new List(temp.Length); string root = GetFullPath(UploadDirectory); foreach(string s in temp) { // root = C:\blah\ - ends with '\' res.Add(s.Substring(root.Length - 1).Replace(Path.DirectorySeparatorChar, '/')); } return res.ToArray(); } /// /// Lists the Directories in the specified directory. /// /// The full directory name, for example "/my/directory". Null, empty or "/" for the root directory. /// The list of Directories in the Directory. /// If does not exist. public string[] ListDirectories(string directory) { string d = BuildFullPath(directory); if(!Directory.Exists(d)) throw new ArgumentException("Directory does not exist", "directory"); string[] temp = Directory.GetDirectories(d); // Result must be transformed in the form /my/dir List res = new List(temp.Length); string root = GetFullPath(UploadDirectory); foreach(string s in temp) { // root = C:\blah\ - ends with '\' res.Add(s.Substring(root.Length - 1).Replace(Path.DirectorySeparatorChar, '/') + "/"); } return res.ToArray(); } /// /// Copies data from a Stream to another. /// /// The Source stream. /// The destination Stream. private static void StreamCopy(Stream source, Stream destination) { byte[] buff = new byte[BufferSize]; int copied = 0; do { copied = source.Read(buff, 0, buff.Length); if(copied > 0) { destination.Write(buff, 0, copied); } } while(copied > 0); } /// /// Stores a file. /// /// The full name of the file. /// A Stream object used as source of a byte stream, /// i.e. the method reads from the Stream and stores the content properly. /// true to overwrite an existing file. /// true if the File is stored, false otherwise. /// If overwrite is false and File already exists, the method returns false. /// If os are null. /// If is empty or does not support reading. public bool StoreFile(string fullName, Stream sourceStream, bool overwrite) { if(fullName == null) throw new ArgumentNullException("fullName"); if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName"); if(sourceStream == null) throw new ArgumentNullException("sourceStream"); if(!sourceStream.CanRead) throw new ArgumentException("Cannot read from Source Stream", "sourceStream"); string filename = BuildFullPath(fullName); // Abort if the file already exists and overwrite is false if(File.Exists(filename) && !overwrite) return false; FileStream fs = null; bool done = false; try { fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); // StreamCopy content (throws exception in case of error) StreamCopy(sourceStream, fs); done = true; } catch(IOException) { done = false; } finally { try { fs.Close(); } catch { } } return done; } /// /// Retrieves a File. /// /// The full name of the File. /// A Stream object used as destination of a byte stream, /// i.e. the method writes to the Stream the file content. /// A value indicating whether or not to count this retrieval in the statistics. /// true if the file is retrieved, false otherwise. /// If os are null. /// If is empty or does not support writing, or if does not exist. public bool RetrieveFile(string fullName, Stream destinationStream, bool countHit) { if(fullName == null) throw new ArgumentNullException("fullName"); if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName"); if(destinationStream == null) throw new ArgumentNullException("destinationStream"); if(!destinationStream.CanWrite) throw new ArgumentException("Cannot write into Destination Stream", "destinationStream"); string filename = BuildFullPath(fullName); if(!File.Exists(filename)) throw new ArgumentException("File does not exist", "fullName"); FileStream fs = null; bool done = false; try { fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); // StreamCopy content (throws exception in case of error) StreamCopy(fs, destinationStream); done = true; } catch(IOException) { done = false; } finally { try { fs.Close(); } catch { } } if(countHit) { AddDownloadHit(fullName, GetFullPath(FileDownloadsFile)); } return done; } /// /// Adds a download hit for the specified item in the specified output file. /// /// The item. /// The full path to the output file. private void AddDownloadHit(string itemName, string outputFile) { lock(this) { string[] lines = File.ReadAllLines(outputFile); string lowercaseItemName = itemName.ToLowerInvariant(); string[] fields; bool found = false; for(int i = 0; i < lines.Length; i++) { fields = lines[i].Split('|'); if(fields[0].ToLowerInvariant() == lowercaseItemName) { int count = 0; int.TryParse(fields[1], out count); count = count + 1; lines[i] = itemName + "|" + count.ToString(); found = true; } } if(!found) { // Add a new line for the current item string[] newLines = new string[lines.Length + 1]; Array.Copy(lines, 0, newLines, 0, lines.Length); newLines[newLines.Length - 1] = itemName + "|1"; lines = newLines; } // Overwrite file with updated data File.WriteAllLines(outputFile, lines); } } /// /// Sets the download hits for the specified item in the specified file. /// /// The item. /// The full path of the output file. /// The hit count to set. private void SetDownloadHits(string itemName, string outputFile, int count) { lock(this) { string[] lines = File.ReadAllLines(outputFile); List outputLines = new List(lines.Length); string lowercaseItemName = itemName.ToLowerInvariant(); string[] fields; foreach(string line in lines) { fields = line.Split('|'); if(fields[0].ToLowerInvariant() == lowercaseItemName) { // Set the new count outputLines.Add(fields[0] + "|" + count.ToString()); } else { // Copy data with no modification outputLines.Add(line); } } File.WriteAllLines(outputFile, outputLines.ToArray()); } } /// /// Clears the download hits for the items that match itemName in the specified file. /// /// The first part of the item name. /// The full path of the output file. private void ClearDownloadHitsPartialMatch(string itemName, string outputFile) { lock(this) { string[] lines = File.ReadAllLines(outputFile); List newLines = new List(lines.Length); string lowercaseItemName = itemName.ToLowerInvariant(); string[] fields; foreach(string line in lines) { fields = line.Split('|'); if(!fields[0].ToLowerInvariant().StartsWith(lowercaseItemName)) { newLines.Add(line); } } File.WriteAllLines(outputFile, newLines.ToArray()); } } /// /// Renames an item of the download count list in the specified file. /// /// The old item name. /// The new item name. /// The full path of the output file. private void RenameDownloadHitsItem(string oldItemName, string newItemName, string outputFile) { lock(this) { string[] lines = File.ReadAllLines(outputFile); string lowercaseOldItemName = oldItemName.ToLowerInvariant(); string[] fields; bool found = false; for(int i = 0; i < lines.Length; i++) { fields = lines[i].Split('|'); if(fields[0].ToLowerInvariant() == lowercaseOldItemName) { lines[i] = newItemName + "|" + fields[1]; found = true; break; } } if(found) { File.WriteAllLines(outputFile, lines); } } } /// /// Renames an item of the download count list in the specified file. /// /// The initial part of the old item name. /// The corresponding initial part of the new item name. /// The full path of the output file. private void RenameDownloadHitsItemPartialMatch(string oldItemName, string newItemName, string outputFile) { lock(this) { string[] lines = File.ReadAllLines(outputFile); string lowercaseOldItemName = oldItemName.ToLowerInvariant(); string[] fields; bool found = false; for(int i = 0; i < lines.Length; i++) { fields = lines[i].Split('|'); if(fields[0].ToLowerInvariant().StartsWith(lowercaseOldItemName)) { lines[i] = newItemName + fields[0].Substring(lowercaseOldItemName.Length) + "|" + fields[1]; found = true; } } if(found) { File.WriteAllLines(outputFile, lines); } } } /// /// Gets the number of times a file was retrieved. /// /// The full name of the file. /// The number of times the file was retrieved. private int GetFileRetrievalCount(string fullName) { if(fullName == null) throw new ArgumentNullException("fullName"); if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName"); lock(this) { // Format // /Full/Path/To/File.txt|DownloadCount string[] lines = File.ReadAllLines(GetFullPath(FileDownloadsFile)); string lowercaseFullName = fullName.ToLowerInvariant(); string[] fields; foreach(string line in lines) { fields = line.Split('|'); if(fields[0].ToLowerInvariant() == lowercaseFullName) { int res = 0; if(int.TryParse(fields[1], out res)) return res; else return 0; } } } return 0; } /// /// Clears the number of times a file was retrieved. /// /// The full name of the file. /// The count to set. /// If is null. /// If is empty. /// If is less than zero. public void SetFileRetrievalCount(string fullName, int count) { if(fullName == null) throw new ArgumentNullException("fullName"); if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName"); if(count < 0) throw new ArgumentOutOfRangeException("count", "Count must be greater than or equal to zero"); SetDownloadHits(fullName, GetFullPath(FileDownloadsFile), 0); } /// /// Gets the details of a file. /// /// The full name of the file. /// The details, or null if the file does not exist. /// If is null. /// If is empty. public FileDetails GetFileDetails(string fullName) { if(fullName == null) throw new ArgumentNullException("fullName"); if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName"); string n = BuildFullPath(fullName); if(!File.Exists(n)) return null; FileInfo fi = new FileInfo(n); return new FileDetails(fi.Length, fi.LastWriteTime, GetFileRetrievalCount(fullName)); } /// /// Deletes a File. /// /// The full name of the File. /// true if the File is deleted, false otherwise. /// If is null. /// If is empty or it does not exist. public bool DeleteFile(string fullName) { if(fullName == null) throw new ArgumentNullException("fullName"); if(fullName.Length == 0) throw new ArgumentException("Full Name cannot be empty", "fullName"); string n = BuildFullPath(fullName); if(!File.Exists(n)) throw new ArgumentException("File does not exist", "fullName"); try { File.Delete(n); SetDownloadHits(fullName, GetFullPath(FileDownloadsFile), 0); return true; } catch(IOException) { return false; } } /// /// Renames or moves a File. /// /// The old full name of the File. /// The new full name of the File. /// true if the File is renamed, false otherwise. /// If or are null. /// If or are empty, or if the old file does not exist, or if the new file already exist. public bool RenameFile(string oldFullName, string newFullName) { if(oldFullName == null) throw new ArgumentNullException("oldFullName"); if(oldFullName.Length == 0) throw new ArgumentException("Old Full Name cannot be empty", "oldFullName"); if(newFullName == null) throw new ArgumentNullException("newFullName"); if(newFullName.Length == 0) throw new ArgumentException("New Full Name cannot be empty", "newFullName"); string oldFilename = BuildFullPath(oldFullName); string newFilename = BuildFullPath(newFullName); if(!File.Exists(oldFilename)) throw new ArgumentException("Old File does not exist", "oldFullName"); if(File.Exists(newFilename)) throw new ArgumentException("New File already exists", "newFullName"); try { File.Move(oldFilename, newFilename); RenameDownloadHitsItem(oldFullName, newFullName, GetFullPath(FileDownloadsFile)); return true; } catch(IOException) { return false; } } /// /// Creates a new Directory. /// /// The path to create the new Directory in. /// The name of the new Directory. /// true if the Directory is created, false otherwise. /// If path is "/my/directory" and name is "newdir", a new directory named "/my/directory/newdir" is created. /// If or are null. /// If is empty or if the directory does not exist, or if the new directory already exists. public bool CreateDirectory(string path, string name) { if(path == null) throw new ArgumentNullException("path"); if(name == null) throw new ArgumentNullException("name"); if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name"); if(!Directory.Exists(BuildFullPath(path))) throw new ArgumentException("Directory does not exist", "path"); string partialPath = path + (!path.EndsWith("/") ? "/" : "") + name; string d = BuildFullPath(partialPath); if(Directory.Exists(d)) throw new ArgumentException("Directory already exists", "name"); try { Directory.CreateDirectory(d); return true; } catch(IOException) { return false; } } /// /// Deletes a Directory and all of its content. /// /// The full path of the Directory. /// true if the Directory is delete, false otherwise. /// If is null. /// If is empty or if it equals '/' or it does not exist. public bool DeleteDirectory(string fullPath) { if(fullPath == null) throw new ArgumentNullException("fullPath"); if(fullPath.Length == 0) throw new ArgumentException("Full Path cannot be empty", "fullPath"); if(fullPath == "/") throw new ArgumentException("Cannot delete the root directory", "fullPath"); string d = BuildFullPath(fullPath); if(!Directory.Exists(d)) throw new ArgumentException("Directory does not exist", "fullPath"); try { Directory.Delete(d, true); // Make sure tht fullPath ends with "/" so that the method does not clear wrong items if(!fullPath.EndsWith("/")) fullPath += "/"; ClearDownloadHitsPartialMatch(fullPath, GetFullPath(FileDownloadsFile)); return true; } catch(IOException) { return false; } } /// /// Renames or moves a Directory. /// /// The old full path of the Directory. /// The new full path of the Directory. /// true if the Directory is renamed, false otherwise. /// If or are null. /// If or are empty or equal to '/', /// or if the old directory does not exist or the new directory already exists. public bool RenameDirectory(string oldFullPath, string newFullPath) { if(oldFullPath == null) throw new ArgumentNullException("oldFullPath"); if(oldFullPath.Length == 0) throw new ArgumentException("Old Full Path cannot be empty", "oldFullPath"); if(oldFullPath == "/") throw new ArgumentException("Cannot rename the root directory", "oldFullPath"); if(newFullPath == null) throw new ArgumentNullException("newFullPath"); if(newFullPath.Length == 0) throw new ArgumentException("New Full Path cannot be empty", "newFullPath"); if(newFullPath == "/") throw new ArgumentException("Cannot rename directory to the root directory", "newFullPath"); string olddir = BuildFullPath(oldFullPath); string newdir = BuildFullPath(newFullPath); if(!Directory.Exists(olddir)) throw new ArgumentException("Directory does not exist", "oldFullPath"); if(Directory.Exists(newdir)) throw new ArgumentException("Directory already exists", "newFullPath"); try { Directory.Move(olddir, newdir); // Make sure that oldFullPath and newFullPath end with "/" so that the method does not rename wrong items if(!oldFullPath.EndsWith("/")) oldFullPath += "/"; if(!newFullPath.EndsWith("/")) newFullPath += "/"; RenameDownloadHitsItemPartialMatch(oldFullPath, newFullPath, GetFullPath(FileDownloadsFile)); return true; } catch(IOException) { return false; } } /// /// Gets the name of the Directory containing the Attachments of a Page. /// /// The Page Info. /// The name of the Directory (not the full path) that contains the Attachments of the specified Page. private string GetPageAttachmentDirectory(PageInfo pageInfo) { // Use the Hash to avoid problems with special chars and the like // Using the hash prevents GetPageWithAttachments to work //return Hash.Compute(pageInfo.FullName); return pageInfo.FullName; } /// /// The the names of the pages with attachments. /// /// The names of the pages with attachments. public string[] GetPagesWithAttachments() { string[] directories = Directory.GetDirectories(GetFullPath(AttachmentsDirectory)); string[] result = new string[directories.Length]; for(int i = 0; i < result.Length; i++) { result[i] = Path.GetFileName(directories[i]); } return result; } /// /// Returns the names of the Attachments of a Page. /// /// The Page Info object that owns the Attachments. /// The names, or an empty list. /// If is null. public string[] ListPageAttachments(PageInfo pageInfo) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); string dir = BuildFullPathForAttachments(GetPageAttachmentDirectory(pageInfo)); if(!Directory.Exists(dir)) return new string[0]; string[] files = Directory.GetFiles(dir); // Result must contain only the filename, not the full path List result = new List(files.Length); foreach(string f in files) { result.Add(Path.GetFileName(f)); } return result.ToArray(); } /// /// Stores a Page Attachment. /// /// The Page Info that owns the Attachment. /// The name of the Attachment, for example "myfile.jpg". /// A Stream object used as source of a byte stream, /// i.e. the method reads from the Stream and stores the content properly. /// true to overwrite an existing Attachment. /// true if the Attachment is stored, false otherwise. /// If overwrite is false and Attachment already exists, the method returns false. /// If , or are null. /// If is empty or if does not support reading. public bool StorePageAttachment(PageInfo pageInfo, string name, Stream sourceStream, bool overwrite) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); if(name == null) throw new ArgumentNullException("name"); if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name"); if(sourceStream == null) throw new ArgumentNullException("sourceStream"); if(!sourceStream.CanRead) throw new ArgumentException("Cannot read from Source Stream", "sourceStream"); string filename = BuildFullPathForAttachments(GetPageAttachmentDirectory(pageInfo) + "/" + name); if(!Directory.Exists(Path.GetDirectoryName(filename))) { try { Directory.CreateDirectory(Path.GetDirectoryName(filename)); } catch(IOException) { // Cannot create attachments dir return false; } } if(File.Exists(filename) && !overwrite) return false; FileStream fs = null; bool done = false; try { fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); // StreamCopy content (throws exception in case of error) StreamCopy(sourceStream, fs); done = true; } catch(IOException) { return false; } finally { try { fs.Close(); } catch { } } return done; } /// /// Retrieves a Page Attachment. /// /// The Page Info that owns the Attachment. /// The name of the Attachment, for example "myfile.jpg". /// A Stream object used as destination of a byte stream, /// i.e. the method writes to the Stream the file content. /// A value indicating whether or not to count this retrieval in the statistics. /// true if the Attachment is retrieved, false otherwise. /// If , or are null. /// If is empty or if does not support writing, /// or if the page does not have attachments or if the attachment does not exist. public bool RetrievePageAttachment(PageInfo pageInfo, string name, Stream destinationStream, bool countHit) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); if(name == null) throw new ArgumentNullException("name"); if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name"); if(destinationStream == null) throw new ArgumentNullException("destinationStream"); if(!destinationStream.CanWrite) throw new ArgumentException("Cannot write into Destination Stream", "destinationStream"); string d = GetPageAttachmentDirectory(pageInfo); if(!Directory.Exists(BuildFullPathForAttachments(d))) throw new ArgumentException("No attachments for Page", "pageInfo"); string filename = BuildFullPathForAttachments(d + "/" + name); if(!File.Exists(filename)) throw new ArgumentException("Attachment does not exist", "name"); FileStream fs = null; bool done = false; try { fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); // StreamCopy content (throws exception in case of error) StreamCopy(fs, destinationStream); done = true; } catch(IOException) { done = false; } finally { try { fs.Close(); } catch { } } if(countHit) { AddDownloadHit(pageInfo.FullName + "." + name, GetFullPath(AttachmentDownloadsFile)); } return done; } /// /// Gets the number of times a page attachment was retrieved. /// /// The page. /// The name of the attachment. /// The number of times the attachment was retrieved. private int GetPageAttachmentRetrievalCount(PageInfo pageInfo, string name) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); if(name == null) throw new ArgumentNullException("name"); if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name"); lock(this) { // Format // PageName.File|DownloadCount string[] lines = File.ReadAllLines(GetFullPath(AttachmentDownloadsFile)); string lowercaseFullName = pageInfo.FullName + "." + name; lowercaseFullName = lowercaseFullName.ToLowerInvariant(); string[] fields; foreach(string line in lines) { fields = line.Split('|'); if(fields[0].ToLowerInvariant() == lowercaseFullName) { int count; if(int.TryParse(fields[1], out count)) return count; else return 0; } } } return 0; } /// /// Set the number of times a page attachment was retrieved. /// /// The page. /// The name of the attachment. /// The count to set. /// If or are null. /// If is empty. /// If is less than zero. public void SetPageAttachmentRetrievalCount(PageInfo pageInfo, string name, int count) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); if(name == null) throw new ArgumentNullException("name"); if(name.Length == 0) throw new ArgumentException("Name cannot be empty"); if(count < 0) throw new ArgumentOutOfRangeException("Count must be greater than or equal to zero", "count"); SetDownloadHits(pageInfo.FullName + "." + name, GetFullPath(AttachmentDownloadsFile), count); } /// /// Gets the details of a page attachment. /// /// The page that owns the attachment. /// The name of the attachment, for example "myfile.jpg". /// The details of the attachment, or null if the attachment does not exist. /// If or are null. /// If is empty. public FileDetails GetPageAttachmentDetails(PageInfo pageInfo, string name) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); if(name == null) throw new ArgumentNullException("name"); if(name.Length == 0) throw new ArgumentException("Name cannot be empty"); string d = GetPageAttachmentDirectory(pageInfo); if(!Directory.Exists(BuildFullPathForAttachments(d))) return null; string filename = BuildFullPathForAttachments(d + "/" + name); if(!File.Exists(filename)) return null; FileInfo fi = new FileInfo(filename); return new FileDetails(fi.Length, fi.LastWriteTime, GetPageAttachmentRetrievalCount(pageInfo, name)); } /// /// Deletes a Page Attachment. /// /// The Page Info that owns the Attachment. /// The name of the Attachment, for example "myfile.jpg". /// true if the Attachment is deleted, false otherwise. /// If or are null. /// If is empty or if the page or attachment do not exist. public bool DeletePageAttachment(PageInfo pageInfo, string name) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); if(name == null) throw new ArgumentNullException("name"); if(name.Length == 0) throw new ArgumentException("Name cannot be empty"); string d = GetPageAttachmentDirectory(pageInfo); if(!Directory.Exists(BuildFullPathForAttachments(d))) throw new ArgumentException("Page does not exist", "pageInfo"); string filename = BuildFullPathForAttachments(d + "/" + name); if(!File.Exists(filename)) throw new ArgumentException("Attachment does not exist", "name"); try { File.Delete(filename); SetDownloadHits(pageInfo.FullName + "." + name, GetFullPath(AttachmentDownloadsFile), 0); return true; } catch(IOException) { return false; } } /// /// Renames a Page Attachment. /// /// The Page Info that owns the Attachment. /// The old name of the Attachment. /// The new name of the Attachment. /// true if the Attachment is renamed, false otherwise. /// If , or are null. /// If , or are empty, /// or if the page or old attachment do not exist, or the new attachment name already exists. public bool RenamePageAttachment(PageInfo pageInfo, string oldName, string newName) { if(pageInfo == null) throw new ArgumentNullException("pageInfo"); if(oldName == null) throw new ArgumentNullException("oldName"); if(oldName.Length == 0) throw new ArgumentException("Old Name cannot be empty", "oldName"); if(newName == null) throw new ArgumentNullException("newName"); if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName"); string d = GetPageAttachmentDirectory(pageInfo); if(!Directory.Exists(BuildFullPathForAttachments(d))) throw new ArgumentException("Page does not exist", "pageInfo"); string oldFilename = BuildFullPathForAttachments(d + "/" + oldName); if(!File.Exists(oldFilename)) throw new ArgumentException("Attachment does not exist", "oldName"); string newFilename = BuildFullPathForAttachments(d + "/" + newName); if(File.Exists(newFilename)) throw new ArgumentException("Attachment already exists", "newName"); try { File.Move(oldFilename, newFilename); RenameDownloadHitsItem(pageInfo.FullName + "." + oldName, pageInfo.FullName + "." + newName, GetFullPath(AttachmentDownloadsFile)); return true; } catch(IOException) { return false; } } /// /// Notifies to the Provider that a Page has been renamed. /// /// The old Page Info object. /// The new Page Info object. /// If or are null /// If the new page is already in use. public void NotifyPageRenaming(PageInfo oldPage, PageInfo newPage) { if(oldPage == null) throw new ArgumentNullException("oldPage"); if(newPage == null) throw new ArgumentNullException("newPage"); string oldName = GetPageAttachmentDirectory(oldPage); string newName = GetPageAttachmentDirectory(newPage); string oldDir = BuildFullPathForAttachments(oldName); string newDir = BuildFullPathForAttachments(newName); if(!Directory.Exists(oldDir)) return; // Nothing to do if(Directory.Exists(newDir)) throw new ArgumentException("New Page already exists", "newPage"); try { Directory.Move(oldDir, newDir); RenameDownloadHitsItemPartialMatch(oldPage.FullName + ".", newPage.FullName + ".", GetFullPath(AttachmentDownloadsFile)); } catch(IOException) { } } } }