UI and opt-in for setting permissions
This commit is contained in:
parent
5459b5fed4
commit
49168cad25
|
@ -284,9 +284,16 @@ namespace NzbDrone.Core.Configuration
|
||||||
set { SetValue("DownloadClientWorkingFolders", value); }
|
set { SetValue("DownloadClientWorkingFolders", value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean SetPermissionsLinux
|
||||||
|
{
|
||||||
|
get { return GetValueBoolean("SetPermissionsLinux", false); }
|
||||||
|
|
||||||
|
set { SetValue("SetPermissionsLinux", value); }
|
||||||
|
}
|
||||||
|
|
||||||
public String FileChmod
|
public String FileChmod
|
||||||
{
|
{
|
||||||
get { return GetValue("FileChmod", "0755"); }
|
get { return GetValue("FileChmod", "0644"); }
|
||||||
|
|
||||||
set { SetValue("FileChmod", value); }
|
set { SetValue("FileChmod", value); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace NzbDrone.Core.Configuration
|
||||||
Boolean EnableFailedDownloadHandling { get; set; }
|
Boolean EnableFailedDownloadHandling { get; set; }
|
||||||
Boolean CreateEmptySeriesFolders { get; set; }
|
Boolean CreateEmptySeriesFolders { get; set; }
|
||||||
void SaveValues(Dictionary<string, object> configValues);
|
void SaveValues(Dictionary<string, object> configValues);
|
||||||
|
Boolean SetPermissionsLinux { get; set; }
|
||||||
String FileChmod { get; set; }
|
String FileChmod { get; set; }
|
||||||
String FolderChmod { get; set; }
|
String FolderChmod { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,18 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
throw new SameFilenameException("File not moved, source and destination are the same", episodeFile.Path);
|
throw new SameFilenameException("File not moved, source and destination are the same", episodeFile.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
_diskProvider.CreateFolder(new FileInfo(destinationFilename).DirectoryName);
|
var directoryName = new FileInfo(destinationFilename).DirectoryName;
|
||||||
|
|
||||||
|
if (_diskProvider.FolderExists(directoryName))
|
||||||
|
{
|
||||||
|
_diskProvider.CreateFolder(directoryName);
|
||||||
|
SetFolderPermissions(directoryName);
|
||||||
|
|
||||||
|
if (!directoryName.PathEquals(series.Path))
|
||||||
|
{
|
||||||
|
SetFolderPermissions(series.Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, destinationFilename);
|
_logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, destinationFilename);
|
||||||
_diskProvider.MoveFile(episodeFile.Path, destinationFilename);
|
_diskProvider.MoveFile(episodeFile.Path, destinationFilename);
|
||||||
|
@ -88,7 +99,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
_logger.Trace("Setting last write time on series folder: {0}", series.Path);
|
_logger.Trace("Setting last write time on series folder: {0}", series.Path);
|
||||||
_diskProvider.SetFolderWriteTime(series.Path, episodeFile.DateAdded);
|
_diskProvider.SetFolderWriteTime(series.Path, episodeFile.DateAdded);
|
||||||
SetFolderPermissions(series.Path);
|
|
||||||
|
|
||||||
if (series.SeasonFolder)
|
if (series.SeasonFolder)
|
||||||
{
|
{
|
||||||
|
@ -96,7 +106,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
|
|
||||||
_logger.Trace("Setting last write time on season folder: {0}", seasonFolder);
|
_logger.Trace("Setting last write time on season folder: {0}", seasonFolder);
|
||||||
_diskProvider.SetFolderWriteTime(seasonFolder, episodeFile.DateAdded);
|
_diskProvider.SetFolderWriteTime(seasonFolder, episodeFile.DateAdded);
|
||||||
SetFolderPermissions(seasonFolder);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +145,11 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
|
|
||||||
private void SetPermissions(string path, string permissions)
|
private void SetPermissions(string path, string permissions)
|
||||||
{
|
{
|
||||||
|
if (!_configService.SetPermissionsLinux)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_diskProvider.SetPermissions(path, permissions);
|
_diskProvider.SetPermissions(path, permissions);
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
|
||||||
{
|
|
||||||
public interface ISetMediaFilePermissions
|
|
||||||
{
|
|
||||||
void SetPermissions(string filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SetMediaFilePermissions : ISetMediaFilePermissions
|
|
||||||
{
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly IConfigService _configService;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public SetMediaFilePermissions(IDiskProvider diskProvider, IConfigService configService, Logger logger)
|
|
||||||
{
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_configService = configService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPermissions(string filename)
|
|
||||||
{
|
|
||||||
if (OsInfo.IsWindows)
|
|
||||||
{
|
|
||||||
//Wrapped in Try/Catch to prevent this from causing issues with remote NAS boxes, the move worked, which is more important.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_diskProvider.InheritFolderPermissions(filename);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (ex is UnauthorizedAccessException || ex is InvalidOperationException)
|
|
||||||
{
|
|
||||||
_logger.Debug("Unable to apply folder permissions to: ", filename);
|
|
||||||
_logger.TraceException(ex.Message, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_diskProvider.SetPermissions(filename, _configService.FileChmod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -290,7 +290,6 @@
|
||||||
<Compile Include="MediaFiles\MediaFileExtensions.cs" />
|
<Compile Include="MediaFiles\MediaFileExtensions.cs" />
|
||||||
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
|
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
|
||||||
<Compile Include="MediaFiles\RenameEpisodeFilePreview.cs" />
|
<Compile Include="MediaFiles\RenameEpisodeFilePreview.cs" />
|
||||||
<Compile Include="MediaFiles\SetMediaFilePermissions.cs" />
|
|
||||||
<Compile Include="Messaging\Commands\CommandExecutor.cs" />
|
<Compile Include="Messaging\Commands\CommandExecutor.cs" />
|
||||||
<Compile Include="Messaging\Commands\ICommandExecutor.cs" />
|
<Compile Include="Messaging\Commands\ICommandExecutor.cs" />
|
||||||
<Compile Include="Messaging\Commands\IExecute.cs" />
|
<Compile Include="Messaging\Commands\IExecute.cs" />
|
||||||
|
|
|
@ -61,6 +61,13 @@ namespace NzbDrone.Mono
|
||||||
|
|
||||||
throw new Exception("Error setting file permissions: " + error);
|
throw new Exception("Error setting file permissions: " + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Syscall.chown(path, Syscall.getuid(), Syscall.getgid()) < 0)
|
||||||
|
{
|
||||||
|
var error = Stdlib.GetLastError();
|
||||||
|
|
||||||
|
throw new Exception("Error setting file owner: " + error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long? GetTotalSize(string path)
|
public override long? GetTotalSize(string path)
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'handlebars'
|
||||||
|
], function (Handlebars) {
|
||||||
|
Handlebars.registerHelper('LinuxOnly', function () {
|
||||||
|
return new Handlebars.SafeString('<i class="icon-linux" title="Linux Only"></i>');
|
||||||
|
});
|
||||||
|
});
|
|
@ -10,6 +10,7 @@ define(
|
||||||
'Handlebars/Helpers/Series',
|
'Handlebars/Helpers/Series',
|
||||||
'Handlebars/Helpers/Quality',
|
'Handlebars/Helpers/Quality',
|
||||||
'Handlebars/Helpers/System',
|
'Handlebars/Helpers/System',
|
||||||
|
'Handlebars/Helpers/Os',
|
||||||
'Handlebars/Handlebars.Debug'
|
'Handlebars/Handlebars.Debug'
|
||||||
], function (Templates) {
|
], function (Templates) {
|
||||||
return function () {
|
return function () {
|
||||||
|
|
|
@ -5,15 +5,17 @@ define(
|
||||||
'marionette',
|
'marionette',
|
||||||
'Settings/MediaManagement/Naming/NamingView',
|
'Settings/MediaManagement/Naming/NamingView',
|
||||||
'Settings/MediaManagement/Sorting/View',
|
'Settings/MediaManagement/Sorting/View',
|
||||||
'Settings/MediaManagement/FileManagement/FileManagementView'
|
'Settings/MediaManagement/FileManagement/FileManagementView',
|
||||||
], function (Marionette, NamingView, SortingView, FileManagementView) {
|
'Settings/MediaManagement/Permissions/PermissionsView'
|
||||||
|
], function (Marionette, NamingView, SortingView, FileManagementView, PermissionsView) {
|
||||||
return Marionette.Layout.extend({
|
return Marionette.Layout.extend({
|
||||||
template: 'Settings/MediaManagement/MediaManagementLayoutTemplate',
|
template: 'Settings/MediaManagement/MediaManagementLayoutTemplate',
|
||||||
|
|
||||||
regions: {
|
regions: {
|
||||||
episodeNaming : '#episode-naming',
|
episodeNaming : '#episode-naming',
|
||||||
sorting : '#sorting',
|
sorting : '#sorting',
|
||||||
fileManagement : '#file-management'
|
fileManagement : '#file-management',
|
||||||
|
permissions : '#permissions'
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
|
@ -25,6 +27,7 @@ define(
|
||||||
this.episodeNaming.show(new NamingView({ model: this.namingSettings }));
|
this.episodeNaming.show(new NamingView({ model: this.namingSettings }));
|
||||||
this.sorting.show(new SortingView({ model: this.settings }));
|
this.sorting.show(new SortingView({ model: this.settings }));
|
||||||
this.fileManagement.show(new FileManagementView({ model: this.settings }));
|
this.fileManagement.show(new FileManagementView({ model: this.settings }));
|
||||||
|
this.permissions.show(new PermissionsView({ model: this.settings }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
<div id="episode-naming"></div>
|
<div id="episode-naming"></div>
|
||||||
<div id="sorting"></div>
|
<div id="sorting"></div>
|
||||||
<div id="file-management"></div>
|
<div id="file-management"></div>
|
||||||
|
<div id="permissions"></div>
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,39 @@
|
||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'marionette',
|
||||||
|
'Mixins/AsModelBoundView',
|
||||||
|
'Mixins/AutoComplete'
|
||||||
|
], function (Marionette, AsModelBoundView) {
|
||||||
|
|
||||||
|
var view = Marionette.ItemView.extend({
|
||||||
|
template: 'Settings/MediaManagement/Permissions/PermissionsViewTemplate',
|
||||||
|
|
||||||
|
ui: {
|
||||||
|
recyclingBin : '.x-path',
|
||||||
|
failedDownloadHandlingCheckbox: '.x-failed-download-handling',
|
||||||
|
failedDownloadOptions : '.x-failed-download-options'
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'change .x-failed-download-handling': '_setFailedDownloadOptionsVisibility'
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
this.ui.recyclingBin.autoComplete('/directories');
|
||||||
|
},
|
||||||
|
|
||||||
|
_setFailedDownloadOptionsVisibility: function () {
|
||||||
|
var checked = this.ui.failedDownloadHandlingCheckbox.prop('checked');
|
||||||
|
if (checked) {
|
||||||
|
this.ui.failedDownloadOptions.slideDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this.ui.failedDownloadOptions.slideUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return AsModelBoundView.call(view);
|
||||||
|
});
|
|
@ -0,0 +1,48 @@
|
||||||
|
<fieldset class="advanced-setting">
|
||||||
|
<legend>Permissions</legend>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Set Permissions</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<label class="checkbox toggle well">
|
||||||
|
<input type="checkbox" name="setPermissionsLinux"/>
|
||||||
|
<p>
|
||||||
|
<span>Yes</span>
|
||||||
|
<span>No</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<span class="help-inline-checkbox">
|
||||||
|
{{LinuxOnly}}
|
||||||
|
<i class="icon-question-sign" title="Should chmod/chown be run when files are imported/renamed?"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">File chmod mask</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" name="fileChmod"/>
|
||||||
|
<span class="help-inline">
|
||||||
|
{{LinuxOnly}}
|
||||||
|
<i class="icon-nd-form-info" title="Octal, applied to media files when imported/renamed by NzbDrone"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Folder chmod mask</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" name="folderChmod"/>
|
||||||
|
<span class="help-inline">
|
||||||
|
{{LinuxOnly}}
|
||||||
|
<i class="icon-nd-form-info" title="Octal, applied to series/season folders created by NzbDrone"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
Loading…
Reference in New Issue