diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/Entities/OwaSupportedBrowsersCollection.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/Entities/OwaSupportedBrowsersCollection.cs new file mode 100644 index 00000000..dd2c09f9 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/Entities/OwaSupportedBrowsersCollection.cs @@ -0,0 +1,57 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using WebsitePanel.WebDav.Core.Config.WebConfigSections; + +namespace WebsitePanel.WebDav.Core.Config.Entities +{ + public class OwaSupportedBrowsersCollection : AbstractConfigCollection, IReadOnlyDictionary + { + private readonly IDictionary _browsers; + + public OwaSupportedBrowsersCollection() + { + _browsers = ConfigSection.OwaSupportedBrowsers.Cast().ToDictionary(x => x.Browser, y => y.Version); + } + + public IEnumerator> GetEnumerator() + { + return _browsers.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int Count + { + get { return _browsers.Count; } + } + + public bool ContainsKey(string browser) + { + return _browsers.ContainsKey(browser); + } + + public bool TryGetValue(string browser, out int version) + { + return _browsers.TryGetValue(browser, out version); + } + + public int this[string browser] + { + get { return ContainsKey(browser) ? _browsers[browser] : 0; } + } + + public IEnumerable Keys + { + get { return _browsers.Keys; } + } + + public IEnumerable Values + { + get { return _browsers.Values; } + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/IWebDavAppConfig.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/IWebDavAppConfig.cs index 9bb3b4d8..d46df2bd 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/IWebDavAppConfig.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/IWebDavAppConfig.cs @@ -12,5 +12,6 @@ namespace WebsitePanel.WebDav.Core.Config FileIconsDictionary FileIcons { get; } HttpErrorsCollection HttpErrors { get; } OfficeOnlineCollection OfficeOnline { get; } + OwaSupportedBrowsersCollection OwaSupportedBrowsers { get; } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OwaSupportedBrowsersElement.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OwaSupportedBrowsersElement.cs new file mode 100644 index 00000000..3274af8b --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OwaSupportedBrowsersElement.cs @@ -0,0 +1,24 @@ +using System.Configuration; + +namespace WebsitePanel.WebDav.Core.Config.WebConfigSections +{ + public class OwaSupportedBrowsersElement : ConfigurationElement + { + private const string BrowserKey = "browser"; + private const string VersionKey = "version"; + + [ConfigurationProperty(BrowserKey, IsKey = true, IsRequired = true)] + public string Browser + { + get { return (string)this[BrowserKey]; } + set { this[BrowserKey] = value; } + } + + [ConfigurationProperty(VersionKey, IsKey = true, IsRequired = true)] + public int Version + { + get { return (int)this[VersionKey]; } + set { this[VersionKey] = value; } + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OwaSupportedBrowsersElementCollection.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OwaSupportedBrowsersElementCollection.cs new file mode 100644 index 00000000..16bad9a7 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OwaSupportedBrowsersElementCollection.cs @@ -0,0 +1,18 @@ +using System.Configuration; + +namespace WebsitePanel.WebDav.Core.Config.WebConfigSections +{ + [ConfigurationCollection(typeof(OwaSupportedBrowsersElement))] + public class OwaSupportedBrowsersElementCollection : ConfigurationElementCollection + { + protected override ConfigurationElement CreateNewElement() + { + return new OwaSupportedBrowsersElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((OwaSupportedBrowsersElement)element).Browser; + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/WebDavExplorerConfigurationSettingsSection.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/WebDavExplorerConfigurationSettingsSection.cs index 1849b692..a51b9460 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/WebDavExplorerConfigurationSettingsSection.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/WebDavExplorerConfigurationSettingsSection.cs @@ -15,6 +15,7 @@ namespace WebsitePanel.WebDavPortal.WebConfigSections private const string ConnectionStringsKey = "appConnectionStrings"; private const string SessionKeysKey = "sessionKeys"; private const string FileIconsKey = "fileIcons"; + private const string OwaSupportedBrowsersKey = "owaSupportedBrowsers"; private const string OfficeOnlineKey = "officeOnline"; public const string SectionName = "webDavExplorerConfigurationSettings"; @@ -75,6 +76,13 @@ namespace WebsitePanel.WebDavPortal.WebConfigSections set { this[FileIconsKey] = value; } } + [ConfigurationProperty(OwaSupportedBrowsersKey, IsDefaultCollection = false)] + public OwaSupportedBrowsersElementCollection OwaSupportedBrowsers + { + get { return (OwaSupportedBrowsersElementCollection)this[OwaSupportedBrowsersKey]; } + set { this[OwaSupportedBrowsersKey] = value; } + } + [ConfigurationProperty(OfficeOnlineKey, IsDefaultCollection = false)] public OfficeOnlineElementCollection OfficeOnline { diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebDavAppConfigManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebDavAppConfigManager.cs index 01eebf42..f6925e5b 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebDavAppConfigManager.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebDavAppConfigManager.cs @@ -18,6 +18,7 @@ namespace WebsitePanel.WebDav.Core.Config FileIcons = new FileIconsDictionary(); HttpErrors = new HttpErrorsCollection(); OfficeOnline = new OfficeOnlineCollection(); + OwaSupportedBrowsers = new OwaSupportedBrowsersCollection(); } public static WebDavAppConfigManager Instance @@ -51,5 +52,6 @@ namespace WebsitePanel.WebDav.Core.Config public FileIconsDictionary FileIcons { get; private set; } public HttpErrorsCollection HttpErrors { get; private set; } public OfficeOnlineCollection OfficeOnline { get; private set; } + public OwaSupportedBrowsersCollection OwaSupportedBrowsers { get; private set; } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs index bac8e2d5..6635c5f9 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs @@ -29,8 +29,13 @@ namespace WebsitePanel.WebDav.Core.Entities.Owa public bool SupportsUpdate { get; set; } [DataMember] public bool UserCanWrite { get; set; } + [DataMember] + public string DownloadUrl { get; set; } + [DataMember] + public bool ReadOnly { get; set; } //[DataMember] - //public bool ReadOnly { get; set; } + //public bool UserCanNotWriteRelative { get; set; } + //[DataMember] //public string SHA256 { get; set; } @@ -61,8 +66,6 @@ namespace WebsitePanel.WebDav.Core.Entities.Owa //[DataMember] //public bool DisableTranslation { get; set; } //[DataMember] - //public string DownloadUrl { get; set; } - //[DataMember] //public string FileSharingUrl { get; set; } //[DataMember] //public string FileUrl { get; set; } @@ -112,8 +115,7 @@ namespace WebsitePanel.WebDav.Core.Entities.Owa //public string TimeZone { get; set; } //[DataMember] //public bool UserCanAttend { get; set; } - //[DataMember] - //public bool UserCanNotWriteRelative { get; set; } + //[DataMember] //public bool UserCanPresent { get; set; } //[DataMember] diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/PutRelativeFile.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/PutRelativeFile.cs new file mode 100644 index 00000000..22890743 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/PutRelativeFile.cs @@ -0,0 +1,10 @@ +namespace WebsitePanel.WebDav.Core.Entities.Owa +{ + public class PutRelativeFile + { + public string Name { get; set; } + public string Url { get; set; } + public string HostViewUrl { get; set; } + public string HostEditUrl { get; set; } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Extensions/StringExtensions.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Extensions/StringExtensions.cs new file mode 100644 index 00000000..37c67e2c --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Extensions/StringExtensions.cs @@ -0,0 +1,12 @@ +namespace WebsitePanel.WebDav.Core.Extensions +{ + public static class StringExtensions + { + public static string ReplaceLast(this string source, string target, string newValue) + { + int index = source.LastIndexOf(target); + string result = source.Remove(index, target.Length).Insert(index, newValue); + return result; + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs index 12f99b70..c2b8f360 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs @@ -9,6 +9,7 @@ namespace WebsitePanel.WebDav.Core.Interfaces.Managers { IEnumerable OpenFolder(string path); bool IsFile(string path); + bool FileExist(string path); byte[] GetFileBytes(string path); void UploadFile(string path, HttpPostedFileBase file); void UploadFile(string path, byte[] bytes); diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs index a6699870..93d17679 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs @@ -190,6 +190,8 @@ namespace WebsitePanel.WebDav.Core.Managers public void DeleteResource(string path) { path = RemoveLeadingFromPath(path, "office365"); + path = RemoveLeadingFromPath(path, "view"); + path = RemoveLeadingFromPath(path, "edit"); path = RemoveLeadingFromPath(path, WspContext.User.OrganizationId); string folderPath = GetFileFolder(path); @@ -226,6 +228,26 @@ namespace WebsitePanel.WebDav.Core.Managers } } + public bool FileExist(string path) + { + try + { + string folder = GetFileFolder(path); + + var resourceName = GetResourceName(path); + + OpenFolder(folder); + + var resource = _currentFolder.GetResource(resourceName); + + return resource != null; + } + catch (InvalidOperationException exception) + { + return false; + } + } + public string GetFileUrl(string path) { try diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltManager.cs index b33ae340..74e3af14 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltManager.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltManager.cs @@ -1,6 +1,8 @@ using System; using System.IO; +using System.Threading; using Cobalt; +using log4net; using WebsitePanel.WebDav.Core.Interfaces.Managers; using WebsitePanel.WebDav.Core.Interfaces.Owa; @@ -11,12 +13,16 @@ namespace WebsitePanel.WebDav.Core.Owa private readonly IWebDavManager _webDavManager; private readonly IWopiFileManager _fileManager; private readonly IAccessTokenManager _tokenManager; + private readonly ILog Log; - public CobaltManager(IWebDavManager webDavManager, IWopiFileManager fileManager, IAccessTokenManager tokenManager) + public CobaltManager(IWebDavManager webDavManager, IWopiFileManager fileManager, + IAccessTokenManager tokenManager) { _webDavManager = webDavManager; _fileManager = fileManager; _tokenManager = tokenManager; + + Log = LogManager.GetLogger(this.GetType()); } public Atom ProcessRequest(int accessTokenId, Stream requestStream) @@ -27,30 +33,69 @@ namespace WebsitePanel.WebDav.Core.Owa var requestBatch = new RequestBatch(); - var cobaltFile = _fileManager.Get(token.FilePath) ?? _fileManager.Create(accessTokenId); - - Object ctx; - ProtocolVersion protocolVersion; - - requestBatch.DeserializeInputFromProtocol(atomRequest, out ctx, out protocolVersion); - - cobaltFile.CobaltEndpoint.ExecuteRequestBatch(requestBatch); - - foreach (var request in requestBatch.Requests) + try { - if (request.GetType() == typeof(PutChangesRequest) && request.PartitionId == FilePartitionId.Content && request.CompletedSuccessfully) - { - using (var saveStream = new MemoryStream()) - { - GenericFdaStream myCobaltStream = new GenericFda(cobaltFile.CobaltEndpoint, null).GetContentStream(); - myCobaltStream.CopyTo(saveStream); + var cobaltFile = _fileManager.Get(token.FilePath) ?? _fileManager.Create(accessTokenId); - _webDavManager.UploadFile(token.FilePath, saveStream.ToArray()); + Object ctx; + ProtocolVersion protocolVersion; + + requestBatch.DeserializeInputFromProtocol(atomRequest, out ctx, out protocolVersion); + cobaltFile.CobaltEndpoint.ExecuteRequestBatch(requestBatch); + + + foreach (var request in requestBatch.Requests) + { + + if (request.GetType() == typeof (PutChangesRequest) && + request.PartitionId == FilePartitionId.Content && request.CompletedSuccessfully) + { + using (var saveStream = new MemoryStream()) + { + CopyStream(cobaltFile, saveStream); + _webDavManager.UploadFile(token.FilePath, saveStream.ToArray()); + } } } + + + return requestBatch.SerializeOutputToProtocol(protocolVersion); } - return requestBatch.SerializeOutputToProtocol(protocolVersion); + catch (Exception e) + { + Log.Error("Cobalt manager Process request", e); + + throw; + } + } + + private void CopyStream(CobaltFile file, Stream stream) + { + var tries = 3; + + for (int i = 0; i < tries; i++) + { + try + { + GenericFdaStream myCobaltStream = new GenericFda(file.CobaltEndpoint, null).GetContentStream(); + + myCobaltStream.CopyTo(stream); + + break; + } + catch (Exception) + { + //unable to read update - save failed + if (i == tries - 1) + { + throw; + } + + //waiting for cobalt completion + Thread.Sleep(50); + } + } } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs index 8400ca09..d5b4bd8d 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs @@ -4,27 +4,38 @@ using System.Linq; using System.Net.Mime; using System.Runtime.Serialization.Json; using System.Text; +using System.Web; using System.Web.Mvc; using WebsitePanel.WebDav.Core.Client; +using WebsitePanel.WebDav.Core.Config; using WebsitePanel.WebDav.Core.Entities.Owa; using WebsitePanel.WebDav.Core.Interfaces.Managers; using WebsitePanel.WebDav.Core.Interfaces.Owa; +using WebsitePanel.WebDav.Core.Interfaces.Security; +using WebsitePanel.WebDav.Core.Security.Authentication.Principals; +using WebsitePanel.WebDav.Core.Security.Authorization.Enums; namespace WebsitePanel.WebDav.Core.Owa { public class WopiServer : IWopiServer { private readonly IWebDavManager _webDavManager; + private readonly IAccessTokenManager _tokenManager; + private readonly IWebDavAuthorizationService _webDavAuthorizationService; - public WopiServer(IWebDavManager webDavManager) + public WopiServer(IWebDavManager webDavManager, IAccessTokenManager tokenManager, IWebDavAuthorizationService webDavAuthorizationService) { _webDavManager = webDavManager; + _tokenManager = tokenManager; + _webDavAuthorizationService = webDavAuthorizationService; } public CheckFileInfo GetCheckFileInfo(string path) { var resource = _webDavManager.GetResource(path); + var readOnly = _webDavAuthorizationService.GetPermissions(WspContext.User, path).HasFlag(WebDavPermissions.Write) == false; + var cFileInfo = new CheckFileInfo { BaseFileName = resource.DisplayName, @@ -38,7 +49,8 @@ namespace WebsitePanel.WebDav.Core.Owa SupportsScenarioLinks = false, SupportsSecureStore = false, SupportsUpdate = true, - UserCanWrite = true + UserCanWrite = !readOnly, + ReadOnly = readOnly }; return cFileInfo; diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Security/Authorization/WebDavAuthorizationService.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Security/Authorization/WebDavAuthorizationService.cs index e632b873..f0c285f3 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Security/Authorization/WebDavAuthorizationService.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Security/Authorization/WebDavAuthorizationService.cs @@ -64,8 +64,8 @@ namespace WebsitePanel.WebDav.Core.Security.Authorization private IEnumerable GetFolderEsPermissions(WspPrincipal principal, string rootFolderName) { - var dictionary = HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.WebDavRootFoldersPermissions] as - Dictionary>; + var dictionary = HttpContext.Current.Session != null ?HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.WebDavRootFoldersPermissions] as + Dictionary> : null; if (dictionary == null) { @@ -80,7 +80,10 @@ namespace WebsitePanel.WebDav.Core.Security.Authorization dictionary.Add(rootFolder.Name, permissions); } - HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.WebDavRootFoldersPermissions] = dictionary; + if (HttpContext.Current.Session != null) + { + HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.WebDavRootFoldersPermissions] = dictionary; + } } return dictionary.ContainsKey(rootFolderName) ? dictionary[rootFolderName] : new ESPermission[0]; @@ -88,14 +91,16 @@ namespace WebsitePanel.WebDav.Core.Security.Authorization private IEnumerable GetUserSecurityGroups(WspPrincipal principal) { - var groups = HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.UserGroupsKey] as - IEnumerable; + var groups = HttpContext.Current.Session != null ? HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.UserGroupsKey] as IEnumerable : null; if (groups == null) { groups = WSP.Services.Organizations.GetSecurityGroupsByMember(principal.ItemId, principal.AccountId); - HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.UserGroupsKey] = groups; + if (HttpContext.Current.Session != null) + { + HttpContext.Current.Session[WebDavAppConfigManager.Instance.SessionKeys.UserGroupsKey] = groups; + } } return groups ?? new ExchangeAccount[0]; diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj index bf910311..9cbc1f07 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj @@ -104,6 +104,7 @@ + @@ -114,6 +115,8 @@ + + @@ -122,11 +125,13 @@ + + diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs index bf2fe2a4..8e4c1bf8 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs @@ -39,11 +39,29 @@ namespace WebsitePanel.WebDavPortal ); routes.MapRoute( - name: FileSystemRouteNames.ShowOfficeOnlinePath, - url: "office365/{org}/{*pathPart}", - defaults: new { controller = "FileSystem", action = "ShowOfficeDocument", pathPart = UrlParameter.Optional } + name: FileSystemRouteNames.DownloadFile, + url: "download-file/{org}/{*pathPart}", + defaults: new { controller = "FileSystem", action = "DownloadFile" } ); + routes.MapRoute( + name: FileSystemRouteNames.ViewOfficeOnline, + url: "office365/view/{org}/{*pathPart}", + defaults: new { controller = "FileSystem", action = "ViewOfficeDocument", pathPart = UrlParameter.Optional } + ); + + routes.MapRoute( + name: FileSystemRouteNames.EditOfficeOnline, + url: "office365/edit/{org}/{*pathPart}", + defaults: new { controller = "FileSystem", action = "EditOfficeDocument", pathPart = UrlParameter.Optional } + ); + + //routes.MapRoute( + // name: FileSystemRouteNames.ShowOfficeOnlinePath, + // url: "office365/{org}/{*pathPart}", + // defaults: new { controller = "FileSystem", action = "ShowOfficeDocument", pathPart = UrlParameter.Optional } + // ); + routes.MapRoute( name: FileSystemRouteNames.ShowAdditionalContent, url: "show-additional-content/{*path}", diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Content/Site.css b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Content/Site.css index c45a408b..3c09f019 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Content/Site.css +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Content/Site.css @@ -67,6 +67,7 @@ textarea { #logout { font-size: 1.2em; color: #9d9d9d; + margin-bottom: 0px; } #logout :hover { @@ -125,6 +126,10 @@ input,div{border-radius:0px!important;} border-color: #2e8bcc; } +.navbar-collapse, .navbar-header { + padding-top: 4px; +} + .navbar-inverse .navbar-brand, .navbar-text, #logout{ color: #FFFFFF; opacity: 0.8; @@ -152,6 +157,7 @@ div#breadcrumb_wrapper a:last-child { float: none; display: inline-block; margin-right: 10px; + vertical-align: top; } .header-portal-title { diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/Api/OwaController.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/Api/OwaController.cs index d2d84295..950be3f9 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/Api/OwaController.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/Api/OwaController.cs @@ -22,6 +22,9 @@ using WebsitePanel.WebDav.Core.Interfaces.Security; using WebsitePanel.WebDav.Core.Security.Cryptography; using WebsitePanel.WebDav.Core.Wsp.Framework; using WebsitePanel.WebDavPortal.Configurations.ControllerConfigurations; +using WebsitePanel.WebDavPortal.Extensions; +using WebsitePanel.WebDavPortal.UI.Routes; +using WebsitePanel.WebDav.Core.Extensions; namespace WebsitePanel.WebDavPortal.Controllers.Api { @@ -53,6 +56,11 @@ namespace WebsitePanel.WebDavPortal.Controllers.Api var fileInfo = _wopiServer.GetCheckFileInfo(token.FilePath); + var urlPart = Url.Route(FileSystemRouteNames.ShowContentPath, new { org = WspContext.User.OrganizationId, pathPart = token.FilePath }); + var url = new Uri(Request.RequestUri, urlPart).ToString(); + + fileInfo.DownloadUrl = url; + return fileInfo; } @@ -105,6 +113,12 @@ namespace WebsitePanel.WebDavPortal.Controllers.Api return new HttpResponseMessage(HttpStatusCode.OK); } + [HttpPost] + public HttpResponseMessage Refresh_Lock(int accessTokenId) + { + return new HttpResponseMessage(HttpStatusCode.OK); + } + [HttpPost] public HttpResponseMessage UnLock(int accessTokenId) { @@ -122,5 +136,56 @@ namespace WebsitePanel.WebDavPortal.Controllers.Api return new HttpResponseMessage(HttpStatusCode.OK); } + + [HttpPost] + public PutRelativeFile Put_Relative(int accessTokenId) + { + var result = new PutRelativeFile(); + + var token = _tokenManager.GetToken(accessTokenId); + + var newFilePath = string.Empty; + + var target = Request.Headers.Contains("X-WOPI-RelativeTarget") ? Request.Headers.GetValues("X-WOPI-RelativeTarget").First() : Request.Headers.GetValues("X-WOPI-SuggestedTarget").First(); + + bool overwrite = Request.Headers.Contains("X-WOPI-RelativeTarget") && Convert.ToBoolean(Request.Headers.GetValues("X-WOPI-OverwriteRelativeTarget").First()); + + if (string.IsNullOrEmpty(target)) + { + throw new HttpResponseException(HttpStatusCode.BadRequest); + } + + if (target.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Count() > 1) + { + var fileName = Path.GetFileName(token.FilePath); + + newFilePath = token.FilePath.ReplaceLast(fileName, target); + } + else + { + newFilePath = Path.ChangeExtension(token.FilePath, target); + } + + if (overwrite == false && _webDavManager.FileExist(newFilePath)) + { + throw new HttpResponseException(HttpStatusCode.Conflict); + } + + var bytes = Request.Content.ReadAsByteArrayAsync().Result; + + _webDavManager.UploadFile(newFilePath, bytes); + + var newToken = _tokenManager.CreateToken(WspContext.User,newFilePath); + + var readUrlPart = Url.Route(FileSystemRouteNames.ViewOfficeOnline, new { org = WspContext.User.OrganizationId, pathPart = newFilePath}); + var writeUrlPart = Url.Route(FileSystemRouteNames.EditOfficeOnline, new { org = WspContext.User.OrganizationId, pathPart = newFilePath }); + + result.HostEditUrl = new Uri(Request.RequestUri, writeUrlPart).ToString(); + result.HostViewUrl = new Uri(Request.RequestUri, readUrlPart).ToString(); ; + result.Name = Path.GetFileName(newFilePath); + result.Url = Url.GenerateWopiUrl(newToken, newFilePath); + + return result; + } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs index 28b6e19f..4b4cf1c2 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Mime; +using System.Security.Policy; using System.Web; using System.Web.Mvc; using System.Web.Routing; @@ -83,12 +84,8 @@ namespace WebsitePanel.WebDavPortal.Controllers } } - public ActionResult ShowOfficeDocument(string org, string pathPart = "") + public ActionResult ShowOfficeDocument(string org, string pathPart, string owaOpenerUri) { - var permissions = _webDavAuthorizationService.GetPermissions(WspContext.User, pathPart); - - var owaOpener = WebDavAppConfigManager.Instance.OfficeOnline.Single(x => x.Extension == Path.GetExtension(pathPart)); - string fileUrl = WebDavAppConfigManager.Instance.WebdavRoot+ org + "/" + pathPart.TrimStart('/'); var accessToken = _tokenManager.CreateToken(WspContext.User, pathPart); @@ -97,11 +94,32 @@ namespace WebsitePanel.WebDavPortal.Controllers string wopiSrc = Server.UrlDecode(url); - string owaOpenerUri = permissions.HasFlag(WebDavPermissions.Write) ? owaOpener.OwaEditor : owaOpener.OwaView; - var uri = string.Format("{0}/{1}WOPISrc={2}&access_token={3}", WebDavAppConfigManager.Instance.OfficeOnline.Url, owaOpenerUri, Server.UrlEncode(wopiSrc), Server.UrlEncode(accessToken.AccessToken.ToString("N"))); - return View(new OfficeOnlineModel(uri, new Uri(fileUrl).Segments.Last())); + string fileName = fileUrl.Split('/').Last(); + + return View("ShowOfficeDocument", new OfficeOnlineModel(uri, fileName)); + } + + public ActionResult ViewOfficeDocument(string org, string pathPart) + { + var owaOpener = WebDavAppConfigManager.Instance.OfficeOnline.Single(x => x.Extension == Path.GetExtension(pathPart)); + + return ShowOfficeDocument(org, pathPart, owaOpener.OwaView); + } + + public ActionResult EditOfficeDocument(string org, string pathPart) + { + var permissions = _webDavAuthorizationService.GetPermissions(WspContext.User, pathPart); + + if (permissions.HasFlag(WebDavPermissions.Write) == false) + { + return new RedirectToRouteResult(FileSystemRouteNames.ViewOfficeOnline, null); + } + + var owaOpener = WebDavAppConfigManager.Instance.OfficeOnline.Single(x => x.Extension == Path.GetExtension(pathPart)); + + return ShowOfficeDocument(org, pathPart, owaOpener.OwaEditor); } [HttpPost] @@ -116,6 +134,26 @@ namespace WebsitePanel.WebDavPortal.Controllers return PartialView("_ResourseCollectionPartial", result); } + [HttpGet] + public ActionResult DownloadFile(string org, string pathPart) + { + if (org != WspContext.User.OrganizationId) + { + return new HttpStatusCodeResult(HttpStatusCode.NoContent); + } + + string fileName = pathPart.Split('/').Last(); + + if (_webdavManager.IsFile(pathPart) == false) + { + throw new Exception(Resources.NotAFile); + } + + var fileBytes = _webdavManager.GetFileBytes(pathPart); + + return File(fileBytes, MediaTypeNames.Application.Octet, fileName); + } + [HttpPost] public ActionResult UploadFile(string org, string pathPart) { diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Extensions/UrlHelperExtensions.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Extensions/UrlHelperExtensions.cs new file mode 100644 index 00000000..b20035dc --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Extensions/UrlHelperExtensions.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Linq; +using System.Web; +using System.Web.Http.Routing; +using WebsitePanel.EnterpriseServer.Base.HostedSolution; +using WebsitePanel.WebDav.Core; +using WebsitePanel.WebDav.Core.Config; +using WebsitePanel.WebDavPortal.UI.Routes; + +namespace WebsitePanel.WebDavPortal.Extensions +{ + public static class UrlHelperExtensions + { + public static String GenerateWopiUrl(this System.Web.Mvc.UrlHelper urlHelper, WebDavAccessToken token, string path) + { + var urlPart = urlHelper.HttpRouteUrl(OwaRouteNames.CheckFileInfo, new { accessTokenId = token.Id }); + + return GenerateWopiUrl(token, urlPart, path); + } + + public static String GenerateWopiUrl(this UrlHelper urlHelper, WebDavAccessToken token, string path) + { + var urlPart = urlHelper.Route(OwaRouteNames.CheckFileInfo, new { accessTokenId = token.Id }); + + return GenerateWopiUrl(token, urlPart, path); + } + + private static string GenerateWopiUrl(WebDavAccessToken token, string urlPart, string path) + { + var url = new Uri(HttpContext.Current.Request.Url, urlPart).ToString(); + + string wopiSrc = HttpUtility.UrlDecode(url); + + return string.Format("{0}&access_token={1}", wopiSrc, token.AccessToken.ToString("N")); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/FileOperations/FileOpenerManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/FileOperations/FileOpenerManager.cs index 1310e0ba..07ae4558 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/FileOperations/FileOpenerManager.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/FileOperations/FileOpenerManager.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Web; using WebsitePanel.WebDav.Core.Config; using WebsitePanel.WebDavPortal.Extensions; @@ -20,10 +22,23 @@ namespace WebsitePanel.WebDavPortal.FileOperations get { FileOpenerType result; - if (_operationTypes.TryGetValue(fileExtension, out result)) + if (_operationTypes.TryGetValue(fileExtension, out result) && CheckBrowserSupport()) return result; return FileOpenerType.Download; } } + + private bool CheckBrowserSupport() + { + var request = HttpContext.Current.Request; + int supportedVersion; + + if (WebDavAppConfigManager.Instance.OwaSupportedBrowsers.TryGetValue(request.Browser.Browser, out supportedVersion) == false) + { + return false; + } + + return supportedVersion <= request.Browser.MajorVersion; + } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.Designer.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.Designer.cs index 8b375ced..0722b254 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.Designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.Designer.cs @@ -159,6 +159,15 @@ namespace WebsitePanel.WebDavPortal.UI { } } + /// + /// Looks up a localized string similar to Not a file.. + /// + public static string NotAFile { + get { + return ResourceManager.GetString("NotAFile", resourceCulture); + } + } + /// /// Looks up a localized string similar to Processing. /// diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.resx b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.resx index 00e0624f..edd8c739 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Resources.resx @@ -150,6 +150,9 @@ No files are selected. + + Not a file. + Processing diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Routes/FileSystemRouteNames.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Routes/FileSystemRouteNames.cs index 9732a57e..78426d31 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Routes/FileSystemRouteNames.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/UI/Routes/FileSystemRouteNames.cs @@ -8,11 +8,16 @@ namespace WebsitePanel.WebDavPortal.UI.Routes public class FileSystemRouteNames { public const string ShowContentPath = "ShowContentRoute"; - public const string ShowOfficeOnlinePath = "ShowOfficeOnlineRoute"; + public const string ShowOfficeOnlinePath_ = "ShowOfficeOnlineRoute"; + public const string ViewOfficeOnline = "ViewOfficeOnlineRoute"; + public const string EditOfficeOnline = "EditOfficeOnlineRoute"; + public const string ShowAdditionalContent = "ShowAdditionalContentRoute"; public const string UploadFile = "UplaodFIleRoute"; public const string DeleteFiles = "DeleteFilesRoute"; + + public const string DownloadFile = "DownloadFileRoute"; } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowContent.cshtml b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowContent.cshtml index 838709d4..a3f507c9 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowContent.cshtml +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowContent.cshtml @@ -9,7 +9,6 @@ @model WebsitePanel.WebDavPortal.Models.ModelForWebDav @{ - var webDavManager = DependencyResolver.Current.GetService(); ViewBag.Title = WebDavAppConfigManager.Instance.ApplicationName; } diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowOfficeDocument.cshtml b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowOfficeDocument.cshtml index a6b460e5..2ace119b 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowOfficeDocument.cshtml +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Views/FileSystem/ShowOfficeDocument.cshtml @@ -7,7 +7,7 @@ - @Model.FileName + @Html.Raw(Model.FileName)