This commit is contained in:
Virtuworks 2015-01-23 08:23:40 -05:00
commit ff598eaad2
39 changed files with 995 additions and 86 deletions

View file

@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using WebsitePanel.EnterpriseServer.Base.HostedSolution;
using WebsitePanel.WebDav.Core.Config.WebConfigSections;
using WebsitePanel.WebDavPortal.WebConfigSections;
namespace WebsitePanel.WebDav.Core.Config.Entities
@ -33,6 +35,26 @@ namespace WebsitePanel.WebDav.Core.Config.Entities
}
}
public string UserGroupsKey
{
get
{
SessionKeysElement sessionKey =
_sessionKeys.FirstOrDefault(x => x.Key == SessionKeysElement.UserGroupsKey);
return sessionKey != null ? sessionKey.Value : null;
}
}
public string WebDavRootFoldersPermissions
{
get
{
SessionKeysElement sessionKey =
_sessionKeys.FirstOrDefault(x => x.Key == SessionKeysElement.WebDavRootFolderPermissionsKey);
return sessionKey != null ? sessionKey.Value : null;
}
}
public string ResourseRenderCount
{
get

View file

@ -1,6 +1,6 @@
using System.Configuration;
namespace WebsitePanel.WebDavPortal.WebConfigSections
namespace WebsitePanel.WebDav.Core.Config.WebConfigSections
{
public class SessionKeysElement : ConfigurationElement
{
@ -10,6 +10,8 @@ namespace WebsitePanel.WebDavPortal.WebConfigSections
public const string AccountInfoKey = "AccountInfoSessionKey";
public const string AuthTicketKey = "AuthTicketKey";
public const string WebDavManagerKey = "WebDavManagerSessionKey";
public const string UserGroupsKey = "UserGroupsKey";
public const string WebDavRootFolderPermissionsKey = "WebDavRootFolderPermissionsKey";
public const string ResourseRenderCountKey = "ResourseRenderCountSessionKey";
public const string ItemIdSessionKey = "ItemId";

View file

@ -4,5 +4,19 @@ namespace WebsitePanel.WebDav.Core.Exceptions
{
public class WebDavException : Exception
{
public WebDavException()
: base() { }
public WebDavException(string message)
: base(message) { }
public WebDavException(string format, params object[] args)
: base(string.Format(format, args)) { }
public WebDavException(string message, Exception innerException)
: base(message, innerException) { }
public WebDavException(string format, Exception innerException, params object[] args)
: base(string.Format(format, args), innerException) { }
}
}

View file

@ -148,7 +148,7 @@ namespace WebsitePanel.WebDav.Core
public IResource GetResource(string name)
{
IHierarchyItem item =
_children.Single(i => i.ItemType == ItemType.Resource && i.DisplayName.Trim('/') == name.Trim('/'));
_children.Single(i => i.DisplayName.Trim('/') == name.Trim('/'));
var resource = new WebDavResource();
resource.SetCredentials(_credentials);
resource.SetHierarchyItem(item);

View file

@ -428,6 +428,15 @@ namespace WebsitePanel.WebDav.Core
_lastModified = lastModified;
}
/// <summary>
/// For internal use only.
/// </summary>
/// <param name="comment"></param>
public void SetItemType(ItemType type)
{
_itemType = type;
}
/// <summary>
/// For internal use only.
/// </summary>
@ -541,6 +550,7 @@ namespace WebsitePanel.WebDav.Core
SetHref(item.Href);
SetLastModified(item.LastModified);
SetProperties(item.Properties);
SetItemType(item.ItemType);
}
}
}

View file

@ -12,5 +12,6 @@ namespace WebsitePanel.WebDav.Core.Interfaces.Managers
void UploadFile(string path, HttpPostedFileBase file);
IResource GetResource(string path);
string GetFileUrl(string path);
void DeleteResource(string path);
}
}

View file

@ -13,6 +13,7 @@ using WebsitePanel.WebDav.Core.Config;
using WebsitePanel.WebDav.Core.Exceptions;
using WebsitePanel.WebDav.Core.Extensions;
using WebsitePanel.WebDav.Core.Interfaces.Managers;
using WebsitePanel.WebDav.Core.Resources;
using WebsitePanel.WebDav.Core.Security.Cryptography;
using WebsitePanel.WebDav.Core.Wsp.Framework;
@ -70,7 +71,7 @@ namespace WebsitePanel.WebDav.Core.Managers
_cryptography.Decrypt(WspContext.User.EncryptedPassword),
WebDavAppConfigManager.Instance.UserDomain);
_currentFolder = _webDavSession.OpenFolder(string.Format("{0}{1}/{2}", WebDavAppConfigManager.Instance.WebdavRoot, WspContext.User.OrganizationId, pathPart));
_currentFolder = _webDavSession.OpenFolder(string.Format("{0}{1}/{2}", WebDavAppConfigManager.Instance.WebdavRoot, WspContext.User.OrganizationId, pathPart.TrimStart('/')));
}
children = _currentFolder.GetChildren().Where(x => !WebDavAppConfigManager.Instance.ElementsRendering.ElementsToIgnore.Contains(x.DisplayName.Trim('/'))).ToArray();
@ -95,15 +96,9 @@ namespace WebsitePanel.WebDav.Core.Managers
OpenFolder(folder);
try
{
IResource resource = _currentFolder.GetResource(resourceName);
return true;
}
catch (Exception e){}
return false;
return resource.ItemType != ItemType.Folder;
}
@ -148,6 +143,27 @@ namespace WebsitePanel.WebDav.Core.Managers
resource.Upload(bytes);
}
public void DeleteResource(string path)
{
path = RemoveLeadingFromPath(path, "office365");
path = RemoveLeadingFromPath(path, WspContext.User.OrganizationId);
string folderPath = GetFileFolder(path);
OpenFolder(folderPath);
var resourceName = GetResourceName(path);
IResource resource = _currentFolder.GetResource(resourceName);
if (resource.ItemType == ItemType.Folder && GetFoldersItemsCount(path) > 0)
{
throw new WebDavException(string.Format(WebDavResources.FolderIsNotEmptyFormat, resource.DisplayName));
}
resource.Delete();
}
public IResource GetResource(string path)
{
try
@ -210,8 +226,20 @@ namespace WebsitePanel.WebDav.Core.Managers
return rootFolders;
}
private int GetFoldersItemsCount(string path)
{
var items = OpenFolder(path);
return items.Count();
}
#region Helpers
private string RemoveLeadingFromPath(string pathPart, string toRemove)
{
return pathPart.StartsWith('/' + toRemove) ? pathPart.Substring(toRemove.Length + 1) : pathPart;
}
private byte[] ReadFully(Stream input)
{
var buffer = new byte[16 * 1024];

View file

@ -0,0 +1,72 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.33440
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace WebsitePanel.WebDav.Core.Resources {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class WebDavResources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal WebDavResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WebsitePanel.WebDav.Core.Resources.WebDavResources", typeof(WebDavResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Folder {0} is not empty..
/// </summary>
internal static string FolderIsNotEmptyFormat {
get {
return ResourceManager.GetString("FolderIsNotEmptyFormat", resourceCulture);
}
}
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="FolderIsNotEmptyFormat" xml:space="preserve">
<value>Folder {0} is not empty.</value>
</data>
</root>

View file

@ -1,5 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebsitePanel.EnterpriseServer.Base.HostedSolution;
using WebsitePanel.Providers.HostedSolution;
using WebsitePanel.WebDav.Core.Config;
using WebsitePanel.WebDav.Core.Interfaces.Security;
using WebsitePanel.WebDav.Core.Security.Authentication.Principals;
using WebsitePanel.WebDav.Core.Security.Authorization.Enums;
@ -27,18 +32,9 @@ namespace WebsitePanel.WebDav.Core.Security.Authorization
var rootFolder = GetRootFolder(path);
var userGroups = WSP.Services.Organizations.GetSecurityGroupsByMember(principal.ItemId, principal.AccountId);
var userGroups = GetUserSecurityGroups(principal);
var rootFolders = WSP.Services.EnterpriseStorage.GetEnterpriseFolders(principal.ItemId);
var esRootFolder = rootFolders.FirstOrDefault(x => x.Name == rootFolder);
if (esRootFolder == null)
{
return WebDavPermissions.None;
}
var permissions = WSP.Services.EnterpriseStorage.GetEnterpriseFolderPermissions(principal.ItemId, esRootFolder.Name);
var permissions = GetFolderEsPermissions(principal, rootFolder);
foreach (var permission in permissions)
{
@ -65,5 +61,44 @@ namespace WebsitePanel.WebDav.Core.Security.Authorization
{
return path.Split(new[]{'/'}, StringSplitOptions.RemoveEmptyEntries)[0];
}
private IEnumerable<ESPermission> GetFolderEsPermissions(WspPrincipal principal, string rootFolderName)
{
var dictionary = HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.WebDavRootFoldersPermissions] as
Dictionary<string, IEnumerable<ESPermission>>;
if (dictionary == null)
{
dictionary = new Dictionary<string, IEnumerable<ESPermission>>();
var rootFolders = WSP.Services.EnterpriseStorage.GetEnterpriseFolders(principal.ItemId);
foreach (var rootFolder in rootFolders)
{
var permissions = WSP.Services.EnterpriseStorage.GetEnterpriseFolderPermissions(principal.ItemId, rootFolder.Name);
dictionary.Add(rootFolder.Name, permissions);
}
HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.WebDavRootFoldersPermissions] = dictionary;
}
return dictionary.ContainsKey(rootFolderName) ? dictionary[rootFolderName] : new ESPermission[0];
}
private IEnumerable<ExchangeAccount> GetUserSecurityGroups(WspPrincipal principal)
{
var groups = HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.UserGroupsKey] as
IEnumerable<ExchangeAccount>;
if (groups == null)
{
groups = WSP.Services.Organizations.GetSecurityGroupsByMember(principal.ItemId, principal.AccountId);
HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.UserGroupsKey] = groups;
}
return groups ?? new ExchangeAccount[0];
}
}
}

View file

@ -147,6 +147,11 @@
<DesignTime>True</DesignTime>
<DependentUpon>HttpErrors.resx</DependentUpon>
</Compile>
<Compile Include="Resources\WebDavResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>WebDavResources.resx</DependentUpon>
</Compile>
<Compile Include="Security\Authorization\Enums\WebDavPermissions.cs" />
<Compile Include="Security\Authorization\WebDavAuthorizationService.cs" />
<Compile Include="Security\Cryptography\CryptoUtils.cs" />
@ -175,6 +180,10 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>HttpErrors.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\WebDavResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>WebDavResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />

View file

@ -27,7 +27,11 @@ namespace WebsitePanel.WebDavPortal
"~/Scripts/appScripts/recalculateResourseHeight.js",
"~/Scripts/appScripts/uploadingData2.js",
"~/Scripts/appScripts/authentication.js",
"~/Scripts/appScripts/dialogs.js"));
"~/Scripts/appScripts/messages.js",
"~/Scripts/appScripts/fileBrowsing.js",
"~/Scripts/appScripts/dialogs.js",
"~/Scripts/appScripts/wsp.js"
));
bundles.Add(new ScriptBundle("~/bundles/authScripts").Include(
"~/Scripts/appScripts/authentication.js"));

View file

@ -42,6 +42,12 @@ namespace WebsitePanel.WebDavPortal
#endregion
routes.MapRoute(
name: FileSystemRouteNames.DeleteFiles,
url: "files-group-action/delete",
defaults: new { controller = "FileSystem", action = "DeleteFiles" }
);
routes.MapRoute(
name: FileSystemRouteNames.UploadFile,
url: "upload-file/{org}/{*pathPart}",

View file

@ -26,6 +26,32 @@ textarea {
.element-container {
margin-bottom: 15px;
text-align:center;
cursor: pointer;
}
.element-container .element {
position: relative;
text-align:center;
}
.selected-file .element {
position: relative;
box-sizing:border-box;
-moz-box-sizing:border-box;
-webkit-box-sizing:border-box;
border: 1px solid rgb(80, 152, 249);
padding: 3px;
}
.selected-file .element div.selected-element-overlay {
position:absolute;
top:0px;
left: 0px;
width:100%;
height:100%;
background:rgb(200, 224, 255);
opacity:0.3;
pointer-events: none;
}
#errorMessage {
@ -55,3 +81,17 @@ textarea {
.modal-vertical-centered {
margin-top: 25%;
}
.file-actions-menu {
margin-top: 10px;
margin-bottom: 15px;
}
.file-actions-menu .file-deletion {
display: none;
}
#message-area {
margin-top: 15px;
}

View file

@ -6,6 +6,8 @@ using WebsitePanel.WebDav.Core.Config;
using WebsitePanel.WebDav.Core.Security.Authentication;
using WebsitePanel.WebDav.Core.Security.Cryptography;
using WebsitePanel.WebDavPortal.Models;
using WebsitePanel.WebDavPortal.Models.Common;
using WebsitePanel.WebDavPortal.Models.Common.Enums;
using WebsitePanel.WebDavPortal.UI.Routes;
using WebsitePanel.WebDav.Core.Interfaces.Security;
using WebsitePanel.WebDav.Core;

View file

@ -19,6 +19,10 @@ using WebsitePanel.WebDavPortal.CustomAttributes;
using WebsitePanel.WebDavPortal.Extensions;
using WebsitePanel.WebDavPortal.Models;
using System.Net;
using WebsitePanel.WebDavPortal.Models.Common;
using WebsitePanel.WebDavPortal.Models.Common.Enums;
using WebsitePanel.WebDavPortal.Models.FileSystem;
using WebsitePanel.WebDavPortal.UI;
using WebsitePanel.WebDavPortal.UI.Routes;
namespace WebsitePanel.WebDavPortal.Controllers
@ -72,7 +76,7 @@ namespace WebsitePanel.WebDavPortal.Controllers
return View(model);
}
catch (UnauthorizedException)
catch (UnauthorizedException e)
{
throw new HttpException(404, "Not Found");
}
@ -121,5 +125,39 @@ namespace WebsitePanel.WebDavPortal.Controllers
return RedirectToRoute(FileSystemRouteNames.ShowContentPath);
}
[HttpPost]
public JsonResult DeleteFiles(IEnumerable<string> filePathes = null)
{
var model = new DeleteFilesModel();
if (filePathes == null)
{
model.AddMessage(MessageType.Error, Resources.NoFilesAreSelected);
return Json(model);
}
foreach (var file in filePathes)
{
try
{
_webdavManager.DeleteResource(Server.UrlDecode(file));
model.DeletedFiles.Add(file);
}
catch (WebDavException exception)
{
model.AddMessage(MessageType.Error, exception.Message);
}
}
if (model.DeletedFiles.Any())
{
model.AddMessage(MessageType.Success, string.Format(Resources.ItemsWasRemovedFormat, model.DeletedFiles.Count));
}
return Json(model);
}
}
}

View file

@ -0,0 +1,21 @@
using System.Reflection;
using System.Web.Mvc;
namespace WebsitePanel.WebDavPortal.CustomAttributes
{
public class FormValueRequiredAttribute : ActionMethodSelectorAttribute
{
private readonly string _submitButtonName;
public FormValueRequiredAttribute(string submitButtonName)
{
_submitButtonName = submitButtonName;
}
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var value = controllerContext.HttpContext.Request.Form[_submitButtonName];
return !string.IsNullOrEmpty(value);
}
}
}

View file

@ -1,10 +1,11 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using WebsitePanel.Providers.HostedSolution;
using WebsitePanel.WebDavPortal.Models.Common;
namespace WebsitePanel.WebDavPortal.Models
{
public class AccountModel
public class AccountModel : BaseModel
{
[Required]
[Display(Name = @"Login")]

View file

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Web.Mvc;
using WebsitePanel.WebDavPortal.Models.Common.Enums;
namespace WebsitePanel.WebDavPortal.Models.Common
{
public class BaseModel
{
public BaseModel()
{
Messages = new List<Message>();
}
public List<Message> Messages { get; private set; }
public void AddMessage(MessageType type, string value)
{
Messages.Add(new Message
{
Type =type,
Value = value
});
}
}
}

View file

@ -0,0 +1,10 @@
namespace WebsitePanel.WebDavPortal.Models.Common.Enums
{
public enum MessageType
{
Success,
Info,
Warning,
Error
}
}

View file

@ -0,0 +1,10 @@
using WebsitePanel.WebDavPortal.Models.Common.Enums;
namespace WebsitePanel.WebDavPortal.Models.Common
{
public class Message
{
public MessageType Type {get;set;}
public string Value { get; set; }
}
}

View file

@ -1,8 +1,9 @@
using System;
using WebsitePanel.WebDavPortal.Models.Common;
namespace WebsitePanel.WebDavPortal.Models
{
public class ErrorModel
public class ErrorModel : BaseModel
{
public int HttpStatusCode { get; set; }
public string Message { get; set; }

View file

@ -0,0 +1,15 @@
using System.Collections.Generic;
using WebsitePanel.WebDavPortal.Models.Common;
namespace WebsitePanel.WebDavPortal.Models.FileSystem
{
public class DeleteFilesModel : BaseModel
{
public DeleteFilesModel()
{
DeletedFiles = new List<string>();
}
public List<string> DeletedFiles { get; set; }
}
}

View file

@ -1,10 +1,11 @@
using System.Collections.Generic;
using WebsitePanel.WebDav.Core.Client;
using WebsitePanel.WebDav.Core.Security.Authorization.Enums;
using WebsitePanel.WebDavPortal.Models.Common;
namespace WebsitePanel.WebDavPortal.Models
{
public class ModelForWebDav
public class ModelForWebDav : BaseModel
{
public IEnumerable<IHierarchyItem> Items { get; set; }
public string UrlSuffix { get; set; }

View file

@ -1,6 +1,8 @@
namespace WebsitePanel.WebDavPortal.Models
using WebsitePanel.WebDavPortal.Models.Common;
namespace WebsitePanel.WebDavPortal.Models
{
public class OfficeOnlineModel
public class OfficeOnlineModel : BaseModel
{
public string Url { get; set; }
public string FileName { get; set; }

View file

@ -1,20 +1,79 @@
var processDialog;
processDialog = processDialog || (function () {
var processDialogDiv = $('#processDialog');
return {
showPleaseWait: function () {
$('#processDialog').modal();
},
hidePleaseWait: function () {
$('#processDialog').modal('hide');
function WspDialogs() {
this.settings = { dialogId: "#confirm-dialog", processDialogId: "#processDialog" };
}
WspDialogs.prototype =
{
showConfirmDialog: function(title, content, positiveButton, positiveClickFunction, dialogId) {
dialogId = dialogId || this.settings.dialogId;
//title replace
if (title) {
$(dialogId).find('.modal-title').empty();
$(dialogId).find('.modal-title').text(title);
}
//body replace
$(dialogId).find('.modal-body').empty();
$(dialogId).find('.modal-body').html(content);
//title replace
if (positiveButton) {
$(dialogId).find('.modal-footer .positive-button').empty();
$(dialogId).find('.modal-footer .positive-button').text(positiveButton);
}
//binding click event
$(dialogId).find('.modal-footer .positive-button').unbind('click');
$(dialogId).find('.modal-footer .positive-button').click(positiveClickFunction);
$(dialogId).modal();
},
showProcessDialog: function() {
$(this.settings.processDialogId).modal();
},
hideProcessDialog: function() {
$(this.settings.processDialogId).modal('hide');
}
};
})();
/*
wsp.dialogs = wsp.dialogs || (function () {
var settings = { dialogId: "#confirm-dialog" };
$(document).ready(function() {
$('.processing-dialog').click(function () {
processDialog.showPleaseWait();
});
});
return {
settings: settings,
showConfirmDialog : function (title, content, positiveButton, positiveClickFunction, dialogId) {
dialogId = dialogId || this.settings.dialogId;
//title replace
if (title) {
$(dialogId).find('.modal-title').empty();
$(dialogId).find('.modal-title').text(title);
}
//body replace
$(dialogId).find('.modal-body').empty();
$(dialogId).find('.modal-body').html(content);
//title replace
if (positiveButton) {
$(dialogId).find('.modal-footer .positive-button').empty();
$(dialogId).find('.modal-footer .positive-button').text(positiveButton);
}
//binding click event
$(dialogId).find('.modal-footer .positive-button').unbind('click');
$(dialogId).find('.modal-footer .positive-button').click(positiveClickFunction);
$(dialogId).modal();
},
showProcessDialog: function () {
$('#processDialog').modal();
}
};
})();*/

View file

@ -0,0 +1,89 @@
function WspFileBrowser() {
this.settings = { deletionBlockSelector: ".file-actions-menu .file-deletion", deletionUrl: "files-group-action/delete1" };
}
WspFileBrowser.prototype = {
setSettings: function(options) {
this.settings = $.extend(this.settings, options);
},
clearAllSelectedItems: function() {
$('.element-container').removeClass("selected-file");
},
selectItem: function(item) {
$(item).addClass("selected-file");
},
openItem: function(item) {
var links = $(item).find('.file-link');
if (links.length != 0) {
links[0].click();
}
},
getSelectedItemsCount: function() {
return $('.element-container.selected-file').length;
},
getSelectedItemsPaths: function() {
return $('.element-container.selected-file a').map(function() {
return $(this).attr('href');
}).get();
},
deleteSelectedItems: function(e) {
$.ajax({
type: 'POST',
url: wsp.fileBrowser.settings.deletionUrl,
data: { filePathes: wsp.fileBrowser.getSelectedItemsPaths() },
dataType: "json",
success: function(model) {
wsp.messages.showMessages(model.Messages);
wsp.fileBrowser.clearDeletedItems(model.DeletedFiles);
wsp.fileBrowser.refreshDeletionBlock();
wsp.dialogs.hideProcessDialog();
},
error: function(jqXHR, textStatus, errorThrown) {
wsp.messages.addErrorMessage(errorThrown);
wsp.fileBrowser.refreshDeletionBlock();
wsp.dialogs.hideProcessDialog();
}
});
wsp.dialogs.showProcessDialog();
},
clearDeletedItems: function(items) {
$.each(items, function(i, item) {
$('.element-container').has('a[href="' + item + '"]').remove();
});
},
refreshDeletionBlock: function() {
if (this.getSelectedItemsCount() > 0) {
$(this.settings.deletionBlockSelector).css('display', 'inline-block');
} else {
$(this.settings.deletionBlockSelector).hide();
}
}
};

View file

@ -0,0 +1,55 @@
function WspMessager(messageDivId) {
this.settings = {
messageDivId: messageDivId,
successClass: "alert-success",
infoClass: "alert-info",
warningClass: "alert-warning",
dangerClass: "alert-danger",
messageDivtemplate: '<div class="alert {0} alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>{1}</div>'
};
}
WspMessager.prototype = {
addMessage: function(cssClass, message) {
var messageDiv = jQuery.validator.format(this.settings.messageDivtemplate, cssClass, message);
$(messageDiv).appendTo(this.settings.messageDivId);
},
addSuccessMessage: function(message) {
this.addMessage(this.settings.successClass, message);
},
addInfoMessage: function(message) {
this.addMessage(this.settings.infoClass, message);
},
addWarningMessage : function (message) {
this.addMessage(this.settings.warningClass, message);
},
addErrorMessage: function (message) {
this.addMessage(this.settings.dangerClass, message);
},
showMessages: function (messages) {
var objthis = this;
$.each(messages, function(i, message) {
if ((message.Type == 0)) {
objthis.addSuccessMessage(message.Value);
}
else if (message.Type == 1) {
objthis.addInfoMessage(message.Value);
}
else if (message.Type == 2) {
objthis.addWarningMessage(message.Value);
}
else if (message.Type == 3) {
objthis.addErrorMessage(message.Value);
}
}
);
}
};

View file

@ -6,6 +6,10 @@
maxHeight = Math.max.apply(null, heights);
if (maxHeight < 135) {
maxHeight = 135;
}
$(".element-container").height(maxHeight);
});
}

View file

@ -0,0 +1,54 @@
var wsp = {
messages: new WspMessager('#message-area'),
fileBrowser: new WspFileBrowser(),
dialogs: new WspDialogs()
};
$(document).ready(function () {
$('.processing-dialog').click(function () {
wsp.dialogs.showProcessDialog();
});
});
//Toggle file select + Ctrl multiselect
$(document).on('click', '.element-container', function (e) {
if (e.ctrlKey) {
$(this).toggleClass("selected-file");
} else {
wsp.fileBrowser.clearAllSelectedItems();
wsp.fileBrowser.selectItem(this);
}
wsp.fileBrowser.refreshDeletionBlock();
});
//Double click file open
$(document).on('dblclick', '.element-container', function (e) {
wsp.fileBrowser.openItem(this);
});
//Delete button click
$(document).on('click', '.file-deletion #delete-button', function (e) {
var dialogId = $(this).data('target');
var buttonText = $(this).data('target-positive-button-text');
var content = $(this).data('target-content');
var title = $(this).data('target-title-text');
content = jQuery.validator.format(content, wsp.fileBrowser.getSelectedItemsCount());
wsp.dialogs.showConfirmDialog(title, content, buttonText, wsp.fileBrowser.deleteSelectedItems, dialogId);
});
$(document).click(function (event) {
if (!$(event.target).closest('.element-container, .prevent-deselect').length) {
wsp.fileBrowser.clearAllSelectedItems();
wsp.fileBrowser.refreshDeletionBlock();
}
})

View file

@ -69,6 +69,15 @@ namespace WebsitePanel.WebDavPortal.UI {
}
}
/// <summary>
/// Looks up a localized string similar to Cancel.
/// </summary>
public static string Cancel {
get {
return ResourceManager.GetString("Cancel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Close.
/// </summary>
@ -78,6 +87,42 @@ namespace WebsitePanel.WebDavPortal.UI {
}
}
/// <summary>
/// Looks up a localized string similar to Confirm.
/// </summary>
public static string Confirm {
get {
return ResourceManager.GetString("Confirm", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>
public static string Delete {
get {
return ResourceManager.GetString("Delete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete File?.
/// </summary>
public static string DeleteFileQuestion {
get {
return ResourceManager.GetString("DeleteFileQuestion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to delete {0} item(s)?.
/// </summary>
public static string DialogsContentConfrimFileDeletion {
get {
return ResourceManager.GetString("DialogsContentConfrimFileDeletion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to File Upload.
/// </summary>
@ -96,6 +141,24 @@ namespace WebsitePanel.WebDavPortal.UI {
}
}
/// <summary>
/// Looks up a localized string similar to {0} items was removed..
/// </summary>
public static string ItemsWasRemovedFormat {
get {
return ResourceManager.GetString("ItemsWasRemovedFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No files are selected..
/// </summary>
public static string NoFilesAreSelected {
get {
return ResourceManager.GetString("NoFilesAreSelected", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Processing.
/// </summary>
@ -122,5 +185,14 @@ namespace WebsitePanel.WebDavPortal.UI {
return ResourceManager.GetString("Upload", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Yes.
/// </summary>
public static string Yes {
get {
return ResourceManager.GetString("Yes", resourceCulture);
}
}
}
}

View file

@ -120,15 +120,36 @@
<data name="Actions" xml:space="preserve">
<value>Actions</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Close" xml:space="preserve">
<value>Close</value>
</data>
<data name="Confirm" xml:space="preserve">
<value>Confirm</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="DeleteFileQuestion" xml:space="preserve">
<value>Delete File?</value>
</data>
<data name="DialogsContentConfrimFileDeletion" xml:space="preserve">
<value>Are you sure you want to delete {0} item(s)?</value>
</data>
<data name="FileUpload" xml:space="preserve">
<value>File Upload</value>
</data>
<data name="GigabyteShort" xml:space="preserve">
<value>Gb</value>
</data>
<data name="ItemsWasRemovedFormat" xml:space="preserve">
<value>{0} items was removed.</value>
</data>
<data name="NoFilesAreSelected" xml:space="preserve">
<value>No files are selected.</value>
</data>
<data name="Processing" xml:space="preserve">
<value>Processing</value>
</data>
@ -138,4 +159,7 @@
<data name="Upload" xml:space="preserve">
<value>Upload</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Yes</value>
</data>
</root>

View file

@ -12,5 +12,7 @@ namespace WebsitePanel.WebDavPortal.UI.Routes
public const string ShowAdditionalContent = "ShowAdditionalContentRoute";
public const string UploadFile = "UplaodFIleRoute";
public const string DeleteFiles = "DeleteFilesRoute";
}
}

View file

@ -12,12 +12,7 @@
var webDavManager = DependencyResolver.Current.GetService<IWebDavManager>();
ViewBag.Title = WebDavAppConfigManager.Instance.ApplicationName;
}
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/appScripts")
<script>
recalculateResourseHeight();
</script>
<br />
@if (Model != null && !string.IsNullOrEmpty(Model.Error))
@ -26,7 +21,7 @@
}
else
{
<div class="container">
<div class="container prevent-deselect">
@if (Model != null)
{
string header = WspContext.User.OrganizationId;
@ -37,25 +32,24 @@ else
<span class="glyphicon glyphicon-chevron-right" style="top: 2px;"></span>
<a href="@string.Concat("/" + header + "/", string.Join("/", elements.Take(i + 1)))" class="btn btn-primary btn-sm active" role="button">@elements[i]</a>
}
if (Model.Permissions.HasFlag(WebDavPermissions.Write))
{
@*<a id="upload-button" class="btn btn-success btn-sm active" data-toggle="modal" data-target="#file-upload" role="button">@Resources.FileUpload</a>*@
<div class="dropdown navbar-right">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
@Resources.Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
<li role="presentation"><a id="upload-button" data-toggle="modal" data-target="#file-upload" role="menuitem" tabindex="-1" href="#">@Resources.FileUpload</a></li>
</ul>
</div>
}
</div>
<div class="container file-actions-menu prevent-deselect">
@if (Model.Permissions.HasFlag(WebDavPermissions.Write))
{
<div class="file-deletion navbar-left">
<a id="delete-button" class="btn btn-danger btn-sm active" role="button"
data-target="#confirm-dialog"
data-target-positive-button-text="@Resources.Delete"
data-target-title-text="@Resources.DeleteFileQuestion"
data-target-content="@Resources.DialogsContentConfrimFileDeletion">@Resources.Delete</a>
</div>
<a id="upload-button" class="btn btn-success btn-sm active navbar-right" data-toggle="modal" data-target="#file-upload" role="button">@Resources.FileUpload</a>
}
</div>
<br />
<div class="container">
<div class="row" id="resourcesDiv">
@if (Model != null)
{
@ -68,6 +62,13 @@ else
</div>
}
@section scripts{
<script>
wsp.fileBrowser.setSettings({ deletionUrl: "@Url.RouteUrl(FileSystemRouteNames.DeleteFiles)" });
recalculateResourseHeight();
</script>
}
@section popups
{
@ -93,4 +94,5 @@ else
</div>
@Html.Partial("_ProcessDialog", null)
@Html.Partial("_ConfirmDialog")
}

View file

@ -17,12 +17,12 @@
{
case FileOpenerType.OfficeOnline:
isTargetBlank = true;
var pathPart = Model.Href.AbsolutePath.Replace("/" + WspContext.User.OrganizationId, "");
var pathPart = Model.Href.AbsolutePath.Replace("/" + WspContext.User.OrganizationId, "").TrimStart('/');
href = string.Concat(Url.RouteUrl(FileSystemRouteNames.ShowOfficeOnlinePath, new { org = WspContext.User.OrganizationId, pathPart = "" }), pathPart);
break;
default:
isTargetBlank = false;
href = Model.Href.AbsolutePath;
href = Model.Href.LocalPath;
break;
}
@ -39,17 +39,24 @@
}
<div class="col-sm-2 element-container">
<a href="@href" @Html.Raw(isTargetBlank ? "target=\"_blank\"" : string.Empty) title="@name">
<div class="element">
<img class="icon-size" src="@Url.Content(actualPath)" />
<a href="@href" @Html.Raw(isTargetBlank ? "target=\"_blank\"" : string.Empty) class="file-link" title="@name">
<p style="word-wrap: break-word;">@name</p>
</a>
@if (showStatistic)
{
<div class="progress web-dav-folder-progress">
<div class="progress-bar" role="progressbar" aria-valuenow="@percent" aria-valuemin="0" aria-valuemax="100" style="width: @percent%;">
@percent%
<p class="progress-text">@percent%</p>
</div>
</div>
<p>@Math.Round(Convert.ToDecimal(resource.ContentLength) / 1024, 2) / @Math.Round(Convert.ToDecimal(resource.AllocatedSpace) / 1024, 2) @Resources.GigabyteShort</p>
}
<div class="selected-element-overlay">
</div>
</div>
</div>

View file

@ -0,0 +1,20 @@
@using WebsitePanel.WebDavPortal.UI
<div class="modal fade" id="confirm-dialog" tabindex="-1" role="dialog" aria-labelledby="confirm-dalog-label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="confirm-dalog-label">@Resources.Confirm</h4>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">@Resources.Cancel</button>
<a href="#" class="btn btn-danger danger positive-button" data-dismiss="modal">@Resources.Yes</a>
</div>
</div>
</div>
</div>

View file

@ -1,9 +1,11 @@
@using Ninject
@using System.Web.Script.Serialization
@using Ninject
@using WebsitePanel.WebDav.Core
@using WebsitePanel.WebDav.Core.Config
@using WebsitePanel.WebDavPortal.DependencyInjection
@using WebsitePanel.WebDavPortal.Models
@using WebsitePanel.WebDavPortal.UI.Routes;
@model WebsitePanel.WebDavPortal.Models.Common.BaseModel
<!DOCTYPE html>
<html>
@ -15,8 +17,8 @@
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar navbar-inverse navbar-fixed-top prevent-deselect">
<div class="container top-container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
@ -37,13 +39,20 @@
</div>
</div>
<div class="container body-content">
<div id="message-area" class="container prevent-deselect"> </div>
@RenderBody()
</div>
<div class="prevent-deselect">
@RenderSection("popups", required: false)
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/appScripts")
@if (WspContext.User != null)
{
@ -54,6 +63,15 @@
</script>
}
@if (Model != null)
{
<script>
wsp.messages.showMessages(@Html.Raw(Json.Encode(Model.Messages)));
</script>
}
@RenderSection("scripts", required: false)

View file

@ -46,8 +46,10 @@
<sessionKeys>
<add key="AccountInfoSessionKey" value="AccountInfo" />
<add key="WebDavManagerSessionKey" value="WebDavManager" />
<add key="WebDavRootFolderPermissionsKey" value="WebDavRootFolderPermissions" />
<add key="ResourseRenderCountSessionKey" value="ResourseRenderCount" />
<add key="ItemIdSessionKey" value="ItemId" />
<add key="UserGroupsKey" value="UserGroups" />
</sessionKeys>
<fileIcons defaultPath="~/Content/Images/other-icon.png">
<add extension=".txt" path="~/Content/Images/txt-icon.png" />

View file

@ -145,6 +145,7 @@
<Compile Include="Controllers\ErrorController.cs" />
<Compile Include="Controllers\FileSystemController.cs" />
<Compile Include="Controllers\OwaController.cs" />
<Compile Include="CustomAttributes\FormValueRequiredAttribute.cs" />
<Compile Include="CustomAttributes\LdapAuthorizationAttribute.cs" />
<Compile Include="DependencyInjection\NinjectDependecyResolver.cs" />
<Compile Include="DependencyInjection\PortalDependencies.cs" />
@ -159,8 +160,12 @@
</Compile>
<Compile Include="HttpHandlers\FileTransferRequestHandler.cs" />
<Compile Include="Models\AccountModel.cs" />
<Compile Include="Models\Common\BaseModel.cs" />
<Compile Include="Models\Common\Enums\MessageType.cs" />
<Compile Include="Models\Common\Message.cs" />
<Compile Include="Models\DirectoryIdentity.cs" />
<Compile Include="Models\ErrorModel.cs" />
<Compile Include="Models\FileSystem\DeleteFilesModel.cs" />
<Compile Include="Models\ModelForWebDav.cs" />
<Compile Include="Models\OfficeOnlineModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -225,8 +230,11 @@
<None Include="Scripts\jquery-2.1.1.intellisense.js" />
<Content Include="Scripts\appScripts\authentication.js" />
<Content Include="Scripts\appScripts\dialogs.js" />
<Content Include="Scripts\appScripts\fileBrowsing.js" />
<Content Include="Scripts\appScripts\messages.js" />
<Content Include="Scripts\appScripts\recalculateResourseHeight.js" />
<Content Include="Scripts\appScripts\uploadingData2.js" />
<Content Include="Scripts\appScripts\wsp.js" />
<Content Include="Scripts\bootstrap.js" />
<Content Include="Scripts\bootstrap.min.js" />
<Content Include="Scripts\jquery-2.1.1.js" />
@ -265,6 +273,7 @@
<Content Include="Views\FileSystem\_ResourseCollectionPartial.cshtml" />
<Content Include="Views\FileSystem\_ResoursePartial.cshtml" />
<Content Include="Views\Shared\_ProcessDialog.cshtml" />
<Content Include="Views\Shared\_ConfirmDialog.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\Owa\" />