989 lines
41 KiB
C#
989 lines
41 KiB
C#
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using ScrewTurn.Wiki.PluginFramework;
|
|
|
|
namespace ScrewTurn.Wiki {
|
|
|
|
/// <summary>
|
|
/// Implements a Local Files Storage Provider.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the Storage Provider.
|
|
/// </summary>
|
|
/// <param name="host">The Host of the Component.</param>
|
|
/// <param name="config">The Configuration data, if any.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="host"/> or <paramref name="config"/> are <c>null</c>.</exception>
|
|
/// <exception cref="InvalidConfigurationException">If <paramref name="config"/> is not valid or is incorrect.</exception>
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method invoked on shutdown.
|
|
/// </summary>
|
|
/// <remarks>This method might not be invoked in some cases.</remarks>
|
|
public void Shutdown() {
|
|
// Nothing to do
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Information about the Provider.
|
|
/// </summary>
|
|
public ComponentInformation Information {
|
|
get { return info; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a brief summary of the configuration string format, in HTML. Returns <c>null</c> if no configuration is needed.
|
|
/// </summary>
|
|
public string ConfigHelpHtml {
|
|
get { return null; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value specifying whether the provider is read-only, i.e. it can only provide data and not store it.
|
|
/// </summary>
|
|
public bool ReadOnly {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds a full path from a provider-specific partial path.
|
|
/// </summary>
|
|
/// <param name="partialPath">The partial path.</param>
|
|
/// <returns>The full path.</returns>
|
|
/// <remarks>For example: if <b>partialPath</b> is "/my/directory", the method returns
|
|
/// "C:\Inetpub\wwwroot\Wiki\public\Upload\my\directory", assuming the Wiki resides in "C:\Inetpub\wwwroot\Wiki".</remarks>
|
|
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
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds a full path from a provider-specific partial path.
|
|
/// </summary>
|
|
/// <param name="partialPath">The partial path.</param>
|
|
/// <returns>The full path.</returns>
|
|
/// <remarks>For example: if <b>partialPath</b> is "/my/directory", the method returns
|
|
/// "C:\Inetpub\wwwroot\Wiki\public\Attachments\my\directory", assuming the Wiki resides in "C:\Inetpub\wwwroot\Wiki".</remarks>
|
|
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
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lists the Files in the specified Directory.
|
|
/// </summary>
|
|
/// <param name="directory">The full directory name, for example "/my/directory". Null, empty or "/" for the root directory.</param>
|
|
/// <returns>The list of Files in the directory.</returns>
|
|
/// <exception cref="ArgumentException">If <paramref name="directory"/> does not exist.</exception>
|
|
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<string> res = new List<string>(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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lists the Directories in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The full directory name, for example "/my/directory". Null, empty or "/" for the root directory.</param>
|
|
/// <returns>The list of Directories in the Directory.</returns>
|
|
/// <exception cref="ArgumentException">If <paramref name="directory"/> does not exist.</exception>
|
|
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<string> res = new List<string>(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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies data from a Stream to another.
|
|
/// </summary>
|
|
/// <param name="source">The Source stream.</param>
|
|
/// <param name="destination">The destination Stream.</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores a file.
|
|
/// </summary>
|
|
/// <param name="fullName">The full name of the file.</param>
|
|
/// <param name="sourceStream">A Stream object used as <b>source</b> of a byte stream,
|
|
/// i.e. the method reads from the Stream and stores the content properly.</param>
|
|
/// <param name="overwrite"><c>true</c> to overwrite an existing file.</param>
|
|
/// <returns><c>true</c> if the File is stored, <c>false</c> otherwise.</returns>
|
|
/// <remarks>If <b>overwrite</b> is <c>false</c> and File already exists, the method returns <c>false</c>.</remarks>
|
|
/// <exception cref="ArgumentNullException">If <typeparamref name="fullName"/> os <paramref name="sourceStream"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or <paramref name="sourceStream"/> does not support reading.</exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves a File.
|
|
/// </summary>
|
|
/// <param name="fullName">The full name of the File.</param>
|
|
/// <param name="destinationStream">A Stream object used as <b>destination</b> of a byte stream,
|
|
/// i.e. the method writes to the Stream the file content.</param>
|
|
/// <param name="countHit">A value indicating whether or not to count this retrieval in the statistics.</param>
|
|
/// <returns><c>true</c> if the file is retrieved, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <typeparamref name="fullName"/> os <paramref name="destinationStream"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or <paramref name="destinationStream"/> does not support writing, or if <paramref name="fullName"/> does not exist.</exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a download hit for the specified item in the specified output file.
|
|
/// </summary>
|
|
/// <param name="itemName">The item.</param>
|
|
/// <param name="outputFile">The full path to the output file.</param>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the download hits for the specified item in the specified file.
|
|
/// </summary>
|
|
/// <param name="itemName">The item.</param>
|
|
/// <param name="outputFile">The full path of the output file.</param>
|
|
/// <param name="count">The hit count to set.</param>
|
|
private void SetDownloadHits(string itemName, string outputFile, int count) {
|
|
lock(this) {
|
|
string[] lines = File.ReadAllLines(outputFile);
|
|
|
|
List<string> outputLines = new List<string>(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());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the download hits for the items that match <b>itemName</b> in the specified file.
|
|
/// </summary>
|
|
/// <param name="itemName">The first part of the item name.</param>
|
|
/// <param name="outputFile">The full path of the output file.</param>
|
|
private void ClearDownloadHitsPartialMatch(string itemName, string outputFile) {
|
|
lock(this) {
|
|
string[] lines = File.ReadAllLines(outputFile);
|
|
|
|
List<string> newLines = new List<string>(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());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames an item of the download count list in the specified file.
|
|
/// </summary>
|
|
/// <param name="oldItemName">The old item name.</param>
|
|
/// <param name="newItemName">The new item name.</param>
|
|
/// <param name="outputFile">The full path of the output file.</param>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames an item of the download count list in the specified file.
|
|
/// </summary>
|
|
/// <param name="oldItemName">The initial part of the old item name.</param>
|
|
/// <param name="newItemName">The corresponding initial part of the new item name.</param>
|
|
/// <param name="outputFile">The full path of the output file.</param>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of times a file was retrieved.
|
|
/// </summary>
|
|
/// <param name="fullName">The full name of the file.</param>
|
|
/// <returns>The number of times the file was retrieved.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the number of times a file was retrieved.
|
|
/// </summary>
|
|
/// <param name="fullName">The full name of the file.</param>
|
|
/// <param name="count">The count to set.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty.</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is less than zero.</exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the details of a file.
|
|
/// </summary>
|
|
/// <param name="fullName">The full name of the file.</param>
|
|
/// <returns>The details, or <c>null</c> if the file does not exist.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty.</exception>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a File.
|
|
/// </summary>
|
|
/// <param name="fullName">The full name of the File.</param>
|
|
/// <returns><c>true</c> if the File is deleted, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="fullName"/> is <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="fullName"/> is empty or it does not exist.</exception>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames or moves a File.
|
|
/// </summary>
|
|
/// <param name="oldFullName">The old full name of the File.</param>
|
|
/// <param name="newFullName">The new full name of the File.</param>
|
|
/// <returns><c>true</c> if the File is renamed, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="oldFullName"/> or <paramref name="newFullName"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="oldFullName"/> or <paramref name="newFullName"/> are empty, or if the old file does not exist, or if the new file already exist.</exception>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new Directory.
|
|
/// </summary>
|
|
/// <param name="path">The path to create the new Directory in.</param>
|
|
/// <param name="name">The name of the new Directory.</param>
|
|
/// <returns><c>true</c> if the Directory is created, <c>false</c> otherwise.</returns>
|
|
/// <remarks>If <b>path</b> is "/my/directory" and <b>name</b> is "newdir", a new directory named "/my/directory/newdir" is created.</remarks>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="path"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if the directory does not exist, or if the new directory already exists.</exception>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a Directory and <b>all of its content</b>.
|
|
/// </summary>
|
|
/// <param name="fullPath">The full path of the Directory.</param>
|
|
/// <returns><c>true</c> if the Directory is delete, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="fullPath"/> is <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="fullPath"/> is empty or if it equals '/' or it does not exist.</exception>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames or moves a Directory.
|
|
/// </summary>
|
|
/// <param name="oldFullPath">The old full path of the Directory.</param>
|
|
/// <param name="newFullPath">The new full path of the Directory.</param>
|
|
/// <returns><c>true</c> if the Directory is renamed, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="oldFullPath"/> or <paramref name="newFullPath"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="oldFullPath"/> or <paramref name="newFullPath"/> are empty or equal to '/',
|
|
/// or if the old directory does not exist or the new directory already exists.</exception>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name of the Directory containing the Attachments of a Page.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The Page Info.</param>
|
|
/// <returns>The name of the Directory (not the full path) that contains the Attachments of the specified Page.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The the names of the pages with attachments.
|
|
/// </summary>
|
|
/// <returns>The names of the pages with attachments.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the names of the Attachments of a Page.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The Page Info object that owns the Attachments.</param>
|
|
/// <returns>The names, or an empty list.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> is <c>null</c>.</exception>
|
|
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<string> result = new List<string>(files.Length);
|
|
foreach(string f in files) {
|
|
result.Add(Path.GetFileName(f));
|
|
}
|
|
return result.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores a Page Attachment.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
|
/// <param name="name">The name of the Attachment, for example "myfile.jpg".</param>
|
|
/// <param name="sourceStream">A Stream object used as <b>source</b> of a byte stream,
|
|
/// i.e. the method reads from the Stream and stores the content properly.</param>
|
|
/// <param name="overwrite"><c>true</c> to overwrite an existing Attachment.</param>
|
|
/// <returns><c>true</c> if the Attachment is stored, <c>false</c> otherwise.</returns>
|
|
/// <remarks>If <b>overwrite</b> is <c>false</c> and Attachment already exists, the method returns <c>false</c>.</remarks>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="name"/> or <paramref name="sourceStream"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if <paramref name="sourceStream"/> does not support reading.</exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves a Page Attachment.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
|
/// <param name="name">The name of the Attachment, for example "myfile.jpg".</param>
|
|
/// <param name="destinationStream">A Stream object used as <b>destination</b> of a byte stream,
|
|
/// i.e. the method writes to the Stream the file content.</param>
|
|
/// <param name="countHit">A value indicating whether or not to count this retrieval in the statistics.</param>
|
|
/// <returns><c>true</c> if the Attachment is retrieved, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="name"/> or <paramref name="destinationStream"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if <paramref name="destinationStream"/> does not support writing,
|
|
/// or if the page does not have attachments or if the attachment does not exist.</exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of times a page attachment was retrieved.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The page.</param>
|
|
/// <param name="name">The name of the attachment.</param>
|
|
/// <returns>The number of times the attachment was retrieved.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the number of times a page attachment was retrieved.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The page.</param>
|
|
/// <param name="name">The name of the attachment.</param>
|
|
/// <param name="count">The count to set.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty.</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="count"/> is less than zero.</exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the details of a page attachment.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The page that owns the attachment.</param>
|
|
/// <param name="name">The name of the attachment, for example "myfile.jpg".</param>
|
|
/// <returns>The details of the attachment, or <c>null</c> if the attachment does not exist.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty.</exception>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a Page Attachment.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
|
/// <param name="name">The name of the Attachment, for example "myfile.jpg".</param>
|
|
/// <returns><c>true</c> if the Attachment is deleted, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/> or <paramref name="name"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="name"/> is empty or if the page or attachment do not exist.</exception>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames a Page Attachment.
|
|
/// </summary>
|
|
/// <param name="pageInfo">The Page Info that owns the Attachment.</param>
|
|
/// <param name="oldName">The old name of the Attachment.</param>
|
|
/// <param name="newName">The new name of the Attachment.</param>
|
|
/// <returns><c>true</c> if the Attachment is renamed, <c>false</c> otherwise.</returns>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="pageInfo"/>, <paramref name="oldName"/> or <paramref name="newName"/> are <c>null</c>.</exception>
|
|
/// <exception cref="ArgumentException">If <paramref name="pageInfo"/>, <paramref name="oldName"/> or <paramref name="newName"/> are empty,
|
|
/// or if the page or old attachment do not exist, or the new attachment name already exists.</exception>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Notifies to the Provider that a Page has been renamed.
|
|
/// </summary>
|
|
/// <param name="oldPage">The old Page Info object.</param>
|
|
/// <param name="newPage">The new Page Info object.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="oldPage"/> or <paramref name="newPage"/> are <c>null</c></exception>
|
|
/// <exception cref="ArgumentException">If the new page is already in use.</exception>
|
|
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) { }
|
|
}
|
|
|
|
}
|
|
|
|
}
|