using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
using ScrewTurn.Wiki.PluginFramework;
namespace ScrewTurn.Wiki.Plugins.SqlCommon {
///
/// Implements a base class for a SQL files storage provider.
///
public abstract class SqlFilesStorageProviderBase : SqlStorageProviderBase, IFilesStorageProviderV30 {
private const int MaxFileSize = 52428800; // 50 MB
#region IFilesStorageProvider Members
///
/// Prepares the directory name.
///
/// The directory to prepare.
/// The prepared directory, for example "/" or "/my/directory/".
private static string PrepareDirectory(string directory) {
if(string.IsNullOrEmpty(directory)) return "/";
else {
return (!directory.StartsWith("/") ? "/" : "") +
directory +
(!directory.EndsWith("/") ? "/" : "");
}
}
///
/// Determines whether a directory exists.
///
/// A database connection.
/// The directory, for example "/my/directory".
/// true if the directory exists, false otherwise.
/// The root directory always exists.
private bool DirectoryExists(DbConnection connection, string directory) {
directory = PrepareDirectory(directory);
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectCountFrom("Directory");
query = queryBuilder.Where(query, "FullPath", WhereOperator.Equals, "FullPath");
List parameters = new List(1);
parameters.Add(new Parameter(ParameterType.String, "FullPath", directory));
DbCommand command = builder.GetCommand(connection, query, parameters);
int count = ExecuteScalar(command, -1, false);
return count == 1;
}
///
/// Determines whether a directory exists.
///
/// A database transaction.
/// The directory, for example "/my/directory".
/// true if the directory exists, false otherwise.
/// The root directory always exists.
private bool DirectoryExists(DbTransaction transaction, string directory) {
directory = PrepareDirectory(directory);
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectCountFrom("Directory");
query = queryBuilder.Where(query, "FullPath", WhereOperator.Equals, "FullPath");
List parameters = new List(1);
parameters.Add(new Parameter(ParameterType.String, "FullPath", directory));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int count = ExecuteScalar(command, -1, false);
return count == 1;
}
///
/// Splits a file full name into the directory and file parts.
///
/// The file full name, for example "/file.txt" or "/directory/file.txt".
/// The resulting directory path, for example "/" or "/directory/".
/// The file name, for example "file.txt".
private static void SplitFileFullName(string fullName, out string directory, out string file) {
directory = fullName.Substring(0, fullName.LastIndexOf("/") + 1);
directory = PrepareDirectory(directory);
file = fullName.Substring(fullName.LastIndexOf("/") + 1);
}
///
/// Determines whether a file exists.
///
/// A database connection.
/// The file full name, for example "/file.txt" or "/directory/file.txt".
/// true if the file exists, false otherwise.
private bool FileExists(DbConnection connection, string fullName) {
string directory, file;
SplitFileFullName(fullName, out directory, out file);
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectCountFrom("File");
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", file));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
DbCommand command = builder.GetCommand(connection, query, parameters);
int count = ExecuteScalar(command, -1, false);
return count == 1;
}
///
/// Determines whether a file exists.
///
/// A database transaction.
/// The file full name, for example "/file.txt" or "/directory/file.txt".
/// true if the file exists, false otherwise.
private bool FileExists(DbTransaction transaction, string fullName) {
string directory, file;
SplitFileFullName(fullName, out directory, out file);
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectCountFrom("File");
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", file));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int count = ExecuteScalar(command, -1, false);
return count == 1;
}
///
/// 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) {
directory = PrepareDirectory(directory);
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
if(!DirectoryExists(connection, directory)) {
CloseConnection(connection);
throw new ArgumentException("Directory does not exist", "directory");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("File", new string[] { "Name" });
query = queryBuilder.Where(query, "Directory", WhereOperator.Equals, "Directory");
query = queryBuilder.OrderBy(query, new [] { "Name" }, new[] { Ordering.Asc });
List parameters = new List(1);
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
DbCommand command = builder.GetCommand(connection, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List result = new List(20);
while(reader.Read()) {
result.Add(directory + reader["Name"] as string);
}
CloseReader(command, reader);
return result.ToArray();
}
else return null;
}
///
/// Lists the Directories in the specified directory.
///
/// An open connection.
/// The full directory name, for example "/my/directory". Null, empty or "/" for the root directory.
/// The list of Directories in the Directory.
private string[] ListDirectories(DbConnection connection, string directory) {
directory = PrepareDirectory(directory);
ICommandBuilder builder = GetCommandBuilder();
if(!DirectoryExists(connection, directory)) {
CloseConnection(connection);
throw new ArgumentException("Directory does not exist", "directory");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("Directory", new string[] { "FullPath" });
query = queryBuilder.Where(query, "Parent", WhereOperator.Equals, "Parent");
query = queryBuilder.OrderBy(query, new[] { "FullPath" }, new[] { Ordering.Asc });
List parameters = new List(1);
parameters.Add(new Parameter(ParameterType.String, "Parent", directory));
DbCommand command = builder.GetCommand(connection, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List result = new List(20);
while(reader.Read()) {
result.Add(reader["FullPath"] as string);
}
CloseReader(reader);
return result.ToArray();
}
else return null;
}
///
/// Lists the Directories in the specified directory.
///
/// A database transaction.
/// The full directory name, for example "/my/directory". Null, empty or "/" for the root directory.
/// The list of Directories in the Directory.
private string[] ListDirectories(DbTransaction transaction, string directory) {
directory = PrepareDirectory(directory);
ICommandBuilder builder = GetCommandBuilder();
if(!DirectoryExists(transaction, directory)) {
RollbackTransaction(transaction);
throw new ArgumentException("Directory does not exist", "directory");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("Directory", new string[] { "FullPath" });
query = queryBuilder.Where(query, "Parent", WhereOperator.Equals, "Parent");
List parameters = new List(1);
parameters.Add(new Parameter(ParameterType.String, "Parent", directory));
query = queryBuilder.OrderBy(query, new[] { "FullPath" }, new[] { Ordering.Asc });
DbCommand command = builder.GetCommand(transaction, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List result = new List(20);
while(reader.Read()) {
result.Add(reader["FullPath"] as string);
}
CloseReader(reader);
return result.ToArray();
}
else return null;
}
///
/// 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) {
directory = PrepareDirectory(directory);
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
if(!DirectoryExists(connection, directory)) {
CloseConnection(connection);
throw new ArgumentException("Directory does not exist", "directory");
}
string[] result = ListDirectories(connection, directory);
CloseConnection(connection);
return result;
}
///
/// 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, System.IO.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 directory, filename;
SplitFileFullName(fullName, out directory, out filename);
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
QueryBuilder queryBuilder = new QueryBuilder(builder);
bool fileExists = FileExists(transaction, fullName);
if(fileExists && !overwrite) {
RollbackTransaction(transaction);
return false;
}
// To achieve decent performance, an UPDATE query is issued if the file exists,
// otherwise an INSERT query is issued
string query;
List parameters;
byte[] fileData = null;
int size = Tools.ReadStream(sourceStream, ref fileData, MaxFileSize);
if(size < 0) {
RollbackTransaction(transaction);
throw new ArgumentException("Source Stream contains too much data", "sourceStream");
}
if(fileExists) {
query = queryBuilder.Update("File", new string[] { "Size", "LastModified", "Data" }, new string[] { "Size", "LastModified", "Data" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
parameters = new List(5);
parameters.Add(new Parameter(ParameterType.Int64, "Size", (long)size));
parameters.Add(new Parameter(ParameterType.DateTime, "LastModified", DateTime.Now));
parameters.Add(new Parameter(ParameterType.ByteArray, "Data", fileData));
parameters.Add(new Parameter(ParameterType.String, "Name", filename));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
}
else {
query = queryBuilder.InsertInto("File", new string[] { "Name", "Directory", "Size", "Downloads", "LastModified", "Data" },
new string[] { "Name", "Directory", "Size", "Downloads", "LastModified", "Data" });
parameters = new List(6);
parameters.Add(new Parameter(ParameterType.String, "Name", filename));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
parameters.Add(new Parameter(ParameterType.Int64, "Size", (long)size));
parameters.Add(new Parameter(ParameterType.Int32, "Downloads", 0));
parameters.Add(new Parameter(ParameterType.DateTime, "LastModified", DateTime.Now));
parameters.Add(new Parameter(ParameterType.ByteArray, "Data", fileData));
}
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows == 1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return rows == 1;
}
///
/// 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.
public bool RetrieveFile(string fullName, System.IO.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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!FileExists(transaction, fullName)) {
RollbackTransaction(transaction);
CloseConnection(connection);
throw new ArgumentException("File does not exist", "fullName");
}
string directory, filename;
SplitFileFullName(fullName, out directory, out filename);
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("File", new string[] { "Size", "Data" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", filename));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
DbCommand command = builder.GetCommand(transaction, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
bool done = false;
if(reader.Read()) {
int read = ReadBinaryColumn(reader, "Data", destinationStream);
done = (long)read == (long)reader["Size"];
}
CloseReader(reader);
if(!done) {
RollbackTransaction(transaction);
return false;
}
}
else {
RollbackTransaction(transaction);
return false;
}
if(countHit) {
// Update download count
query = queryBuilder.UpdateIncrement("File", "Downloads", 1);
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", filename));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows != 1) {
RollbackTransaction(transaction);
return false;
}
}
CommitTransaction(transaction);
return true;
}
///
/// Sets 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");
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string directory, filename;
SplitFileFullName(fullName, out directory, out filename);
string query = queryBuilder.Update("File", new string[] { "Downloads" }, new string[] { "Downloads" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", filename));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
parameters.Add(new Parameter(ParameterType.Int32, "Downloads", count));
DbCommand command = builder.GetCommand(connString, query, parameters);
ExecuteNonQuery(command);
}
///
/// 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");
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string directory, filename;
SplitFileFullName(fullName, out directory, out filename);
string query = queryBuilder.SelectFrom("File", new string[] { "Size", "Downloads", "LastModified" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", filename));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
DbCommand command = builder.GetCommand(connString, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
FileDetails details = null;
if(reader.Read()) {
details = new FileDetails((long)reader["Size"],
(DateTime)reader["LastModified"], (int)reader["Downloads"]);
}
CloseReader(command, reader);
return details;
}
else return null;
}
///
/// 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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!FileExists(transaction, fullName)) {
RollbackTransaction(transaction);
throw new ArgumentException("File does not exist", "fullName");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string directory, filename;
SplitFileFullName(fullName, out directory, out filename);
string query = queryBuilder.DeleteFrom("File");
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "Directory");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", filename));
parameters.Add(new Parameter(ParameterType.String, "Directory", directory));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows == 1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return rows == 1;
}
///
/// 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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
QueryBuilder queryBuilder = new QueryBuilder(builder);
if(!FileExists(transaction, oldFullName)) {
RollbackTransaction(transaction);
throw new ArgumentException("File does not exist", "oldFullName");
}
if(FileExists(transaction, newFullName)) {
RollbackTransaction(transaction);
throw new ArgumentException("File already exists", "newFullPath");
}
string oldDirectory, newDirectory, oldFilename, newFilename;
SplitFileFullName(oldFullName, out oldDirectory, out oldFilename);
SplitFileFullName(newFullName, out newDirectory, out newFilename);
string query = queryBuilder.Update("File", new string[] { "Name" }, new string[] { "NewName" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "OldName");
query = queryBuilder.AndWhere(query, "Directory", WhereOperator.Equals, "OldDirectory");
List parameters = new List(3);
parameters.Add(new Parameter(ParameterType.String, "NewName", newFilename));
parameters.Add(new Parameter(ParameterType.String, "OldName", oldFilename));
parameters.Add(new Parameter(ParameterType.String, "OldDirectory", oldDirectory));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows == 1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return rows == 1;
}
///
/// 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");
path = PrepareDirectory(path);
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!DirectoryExists(transaction, path)) {
RollbackTransaction(transaction);
throw new ArgumentException("Directory does not exist", "path");
}
string newDirectoryFullPath = PrepareDirectory(path + name);
if(DirectoryExists(transaction, newDirectoryFullPath)) {
RollbackTransaction(transaction);
throw new ArgumentException("Directory already exists", "name");
}
string query = QueryBuilder.NewQuery(builder).InsertInto("Directory", new string[] { "FullPath", "Parent" }, new string[] { "FullPath", "Parent" });
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "FullPath", newDirectoryFullPath));
parameters.Add(new Parameter(ParameterType.String, "Parent", path));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows == 1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return rows == 1;
}
///
/// Deletes a directory and all its contents.
///
/// The current transaction to use.
/// The full path of the directory.
/// true if the directory is deleted, false otherwise.
private bool DeleteDirectory(DbTransaction transaction, string fullPath) {
string[] dirs = ListDirectories(transaction, fullPath);
foreach(string dir in dirs) {
if(!DeleteDirectory(transaction, dir)) {
return false;
}
}
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.DeleteFrom("Directory");
query = queryBuilder.Where(query, "FullPath", WhereOperator.Equals, "FullPath");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "FullPath", fullPath));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
return rows > 0;
}
///
/// Deletes a Directory and all of its content.
///
/// The full path of the Directory.
/// true if the Directory is deleted, 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");
fullPath = PrepareDirectory(fullPath);
// /dir/
// /dir/sub/
// /dir/sub/blah/
// /dir/file.txt
// /dir/sub/file.txt
// etc.
// 1. Delete sub-directories (recursively, depth first)
// 2. Delete directory
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!DirectoryExists(transaction, fullPath)) {
RollbackTransaction(transaction);
throw new ArgumentException("Directory does not exist");
}
bool done = DeleteDirectory(transaction, fullPath);
if(done) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return done;
}
///
/// Renames or moves a Directory.
///
/// The current transaction to use.
/// The old full path of the Directory.
/// The new full path of the Directory.
/// true if the Directory is renamed, false otherwise.
private bool RenameDirectory(DbTransaction transaction, string oldFullPath, string newFullPath) {
string[] directories = ListDirectories(transaction, oldFullPath);
foreach(string dir in directories) {
string trimmed = dir.Trim('/');
string name = trimmed.Substring(trimmed.LastIndexOf("/") + 1);
string newFullPathSub = PrepareDirectory(newFullPath + name);
RenameDirectory(dir, newFullPathSub);
}
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.Update("Directory", new string[] { "FullPath" }, new string[] { "NewDirectory1" });
query = queryBuilder.Where(query, "FullPath", WhereOperator.Equals, "OldDirectory1");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "NewDirectory1", newFullPath));
parameters.Add(new Parameter(ParameterType.String, "OldDirectory1", oldFullPath));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
return rows > 0;
}
///
/// 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");
oldFullPath = PrepareDirectory(oldFullPath);
newFullPath = PrepareDirectory(newFullPath);
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!DirectoryExists(transaction, oldFullPath)) {
RollbackTransaction(transaction);
throw new ArgumentException("Directory does not exist", "oldFullPath");
}
if(DirectoryExists(transaction, newFullPath)) {
RollbackTransaction(transaction);
throw new ArgumentException("Directory already exists", "newFullPath");
}
// /dir/
// /dir/sub/
// /dir/sub/blah/
// /dir/file.txt
// /dir/sub/file.txt
// etc.
// 1. Rename sub-directories (recursively, depth first)
// 2. Rename directory
bool done = RenameDirectory(transaction, oldFullPath, newFullPath);
if(done) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return done;
}
///
/// The the names of the pages with attachments.
///
/// The names of the pages with attachments.
public string[] GetPagesWithAttachments() {
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("Attachment", new string[] { "Page" });
query = queryBuilder.GroupBy(query, new[] { "Page" });
query = queryBuilder.OrderBy(query, new[] { "Page" }, new[] { Ordering.Asc });
DbCommand command = builder.GetCommand(connString, query, new List());
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List result = new List(100);
while(reader.Read()) {
result.Add(reader["Page"] as string);
}
CloseReader(command, reader);
return result.ToArray();
}
else return null;
}
///
/// Returns the names of the Attachments of a Page.
///
/// A database transaction.
/// The Page Info object that owns the Attachments.
/// The names, or an empty list.
private string[] ListPageAttachments(DbTransaction transaction, PageInfo pageInfo) {
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("Attachment", new string[] { "Name" });
query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
query = queryBuilder.OrderBy(query, new[] { "Name" }, new[] { Ordering.Asc });
List parameters = new List(1);
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
DbCommand command = builder.GetCommand(transaction, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List result = new List(10);
while(reader.Read()) {
result.Add(reader["Name"] as string);
}
CloseReader(reader);
return result.ToArray();
}
else return null;
}
///
/// Returns the names of the Attachments of a Page.
///
/// A database connection.
/// The Page Info object that owns the Attachments.
/// The names, or an empty list.
private string[] ListPageAttachments(DbConnection connection, PageInfo pageInfo) {
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("Attachment", new string[] { "Name" });
query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(1);
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
query = queryBuilder.OrderBy(query, new[] { "Name" }, new[] { Ordering.Asc });
DbCommand command = builder.GetCommand(connection, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List result = new List(10);
while(reader.Read()) {
result.Add(reader["Name"] as string);
}
CloseReader(reader);
return result.ToArray();
}
else return null;
}
///
/// 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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
string[] result = ListPageAttachments(connection, pageInfo);
CloseConnection(connection);
return result;
}
///
/// Determines whether a page attachment exists.
///
/// A database connection.
/// The page.
/// The attachment.
/// true if the attachment exists, false otherwise.
private bool AttachmentExists(DbConnection connection, PageInfo page, string name) {
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectCountFrom("Attachment");
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", page.FullName));
DbCommand command = builder.GetCommand(connection, query, parameters);
int count = ExecuteScalar(command, -1, false);
return count == 1;
}
///
/// Determines whether a page attachment exists.
///
/// A database transaction.
/// The page.
/// The attachment.
/// true if the attachment exists, false otherwise.
private bool AttachmentExists(DbTransaction transaction, PageInfo page, string name) {
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectCountFrom("Attachment");
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", page.FullName));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int count = ExecuteScalar(command, -1, false);
return count == 1;
}
///
/// 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, System.IO.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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
bool attachmentExists = AttachmentExists(transaction, pageInfo, name);
if(attachmentExists && !overwrite) {
RollbackTransaction(transaction);
return false;
}
// To achieve decent performance, an UPDATE query is issued if the attachment exists,
// otherwise an INSERT query is issued
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query;
List parameters;
byte[] attachmentData = null;
int size = Tools.ReadStream(sourceStream, ref attachmentData, MaxFileSize);
if(size < 0) {
RollbackTransaction(transaction);
throw new ArgumentException("Source Stream contains too much data", "sourceStream");
}
if(attachmentExists) {
query = queryBuilder.Update("Attachment", new string[] { "Size", "LastModified", "Data" }, new string[] { "Size", "LastModified", "Data" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
parameters = new List(5);
parameters.Add(new Parameter(ParameterType.Int64, "Size", (long)size));
parameters.Add(new Parameter(ParameterType.DateTime, "LastModified", DateTime.Now));
parameters.Add(new Parameter(ParameterType.ByteArray, "Data", attachmentData));
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
}
else {
query = queryBuilder.InsertInto("Attachment", new string[] { "Name", "Page", "Size", "Downloads", "LastModified", "Data" },
new string[] { "Name", "Page", "Size", "Downloads", "LastModified", "Data" });
parameters = new List(6);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
parameters.Add(new Parameter(ParameterType.Int64, "Size", (long)size));
parameters.Add(new Parameter(ParameterType.Int32, "Downloads", 0));
parameters.Add(new Parameter(ParameterType.DateTime, "LastModified", DateTime.Now));
parameters.Add(new Parameter(ParameterType.ByteArray, "Data", attachmentData));
}
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows == 1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return rows == 1;
}
///
/// 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, System.IO.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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!AttachmentExists(transaction, pageInfo, name)) {
RollbackTransaction(transaction);
throw new ArgumentException("Attachment does not exist", "name");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("Attachment", new string[] { "Size", "Data" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
DbCommand command = builder.GetCommand(transaction, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
bool done = false;
if(reader.Read()) {
int read = ReadBinaryColumn(reader, "Data", destinationStream);
done = (long)read == (long)reader["Size"];
}
CloseReader(reader);
if(!done) {
RollbackTransaction(transaction);
return false;
}
}
else {
RollbackTransaction(transaction);
return false;
}
if(countHit) {
// Update download count
query = queryBuilder.UpdateIncrement("Attachment", "Downloads", 1);
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows != 1) {
RollbackTransaction(transaction);
return false;
}
}
CommitTransaction(transaction);
return true;
}
///
/// Sets 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");
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.Update("Attachment", new string[] { "Downloads" }, new string[] { "Downloads" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
parameters.Add(new Parameter(ParameterType.Int32, "Downloads", count));
DbCommand command = builder.GetCommand(connString, query, parameters);
ExecuteNonQuery(command);
}
///
/// 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");
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.SelectFrom("Attachment", new string[] { "Size", "Downloads", "LastModified" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
DbCommand command = builder.GetCommand(connString, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
FileDetails details = null;
if(reader.Read()) {
details = new FileDetails((long)reader["Size"],
(DateTime)reader["LastModified"], (int)reader["Downloads"]);
}
CloseReader(command, reader);
return details;
}
else return null;
}
///
/// 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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!AttachmentExists(transaction, pageInfo, name)) {
RollbackTransaction(transaction);
throw new ArgumentException("Attachment does not exist", "name");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.DeleteFrom("Attachment");
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "Name");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "Name", name));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows == 1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return rows == 1;
}
///
/// 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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(!AttachmentExists(transaction, pageInfo, oldName)) {
RollbackTransaction(transaction);
throw new ArgumentException("Attachment does not exist", "name");
}
if(AttachmentExists(transaction, pageInfo, newName)) {
RollbackTransaction(transaction);
throw new ArgumentException("Attachment already exists", "name");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.Update("Attachment", new string[] { "Name" }, new string[] { "NewName" });
query = queryBuilder.Where(query, "Name", WhereOperator.Equals, "OldName");
query = queryBuilder.AndWhere(query, "Page", WhereOperator.Equals, "Page");
List parameters = new List(3);
parameters.Add(new Parameter(ParameterType.String, "NewName", newName));
parameters.Add(new Parameter(ParameterType.String, "OldName", oldName));
parameters.Add(new Parameter(ParameterType.String, "Page", pageInfo.FullName));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows == 1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
return rows == 1;
}
///
/// Notifies 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");
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
if(ListPageAttachments(transaction, newPage).Length > 0) {
RollbackTransaction(transaction);
throw new ArgumentException("New Page already exists", "newPage");
}
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.Update("Attachment", new string[] { "Page" }, new string[] { "NewPage" });
query = queryBuilder.Where(query, "Page", WhereOperator.Equals, "OldPage");
List parameters = new List(2);
parameters.Add(new Parameter(ParameterType.String, "NewPage", newPage.FullName));
parameters.Add(new Parameter(ParameterType.String, "OldPage", oldPage.FullName));
DbCommand command = builder.GetCommand(transaction, query, parameters);
int rows = ExecuteNonQuery(command, false);
if(rows != -1) CommitTransaction(transaction);
else RollbackTransaction(transaction);
}
#endregion
#region IStorageProvider Members
///
/// 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; }
}
#endregion
}
}