Commit acfc3947 authored by Mario's avatar Mario
Browse files

files_ng: implement actions on multiple selected files

parent db10261f
Pipeline #371186 passed with stage
in 5 minutes and 26 seconds
......@@ -18,22 +18,34 @@ class Attach_edit extends Controller {
return;
}
$attach_id = ((x($_POST, 'attach_id')) ? intval($_POST['attach_id']) : null);
$channel_id = ((x($_POST, 'channel_id')) ? intval($_POST['channel_id']) : null);
$dnd = ((x($_POST, 'dnd')) ? intval($_POST['dnd']) : null);
$nick = ((x($_POST, 'nick')) ? notags($_POST['nick']) : '');
$attach_ids = ((x($_POST, 'attach_ids')) ? $_POST['attach_ids'] : []);
$attach_id = ((x($_POST, 'attach_id')) ? intval($_POST['attach_id']) : 0);
$channel_id = ((x($_POST, 'channel_id')) ? intval($_POST['channel_id']) : 0);
$dnd = ((x($_POST, 'dnd')) ? intval($_POST['dnd']) : 0);
$permissions = ((x($_POST, 'permissions')) ? intval($_POST['permissions']) : 0);
//$nick = ((x($_POST, 'nick')) ? notags($_POST['nick']) : '');
$return_path = ((x($_POST, 'return_path')) ? notags($_POST['return_path']) : 'cloud');
$delete = ((x($_POST, 'delete')) ? intval($_POST['delete']) : 0);
$newfolder = ((x($_POST, 'newfolder_' . $attach_id)) ? notags($_POST['newfolder_' . $attach_id]) : '');
if(! $newfolder)
$newfolder = ((x($_POST, 'newfolder')) ? notags($_POST['newfolder']) : '');
$newfilename = ((x($_POST, 'newfilename_' . $attach_id)) ? notags($_POST['newfilename_' . $attach_id]) : '');
$recurse = ((x($_POST, 'recurse_' . $attach_id)) ? intval($_POST['recurse_' . $attach_id]) : 0);
if(! $recurse)
$recurse = ((x($_POST, 'recurse')) ? intval($_POST['recurse']) : 0);
$notify = ((x($_POST, 'notify_edit_' . $attach_id)) ? intval($_POST['notify_edit_' . $attach_id]) : 0);
$copy = ((x($_POST, 'copy_' . $attach_id)) ? intval($_POST['copy_' . $attach_id]) : 0);
if(! $copy)
$copy = ((x($_POST, 'copy')) ? intval($_POST['copy']) : 0);
$categories = ((x($_POST, 'categories_' . $attach_id)) ? notags($_POST['categories_' . $attach_id]) : '');
if(! $categories)
$categories = ((x($_POST, 'categories')) ? notags($_POST['categories']) : '');
if (! $attach_id) {
notice(t('File not found.') . EOL);
return;
}
if($attach_id)
$attach_ids[] = $attach_id;
$single = ((count($attach_ids) === 1) ? true : false);
$channel = channelx_by_n($channel_id);
......@@ -45,9 +57,12 @@ class Attach_edit extends Controller {
$nick = $channel['channel_address'];
$observer = App::get_observer();
$observer_hash = (($observer) ? $observer['xchan_hash'] : '');
$is_owner = ((local_channel() == $channel_id) ? true : false);
$ids_str = implode(',', $attach_ids);
$r = q("SELECT uid, hash, creator, folder, filename, is_photo FROM attach WHERE id = %d AND uid = %d",
intval($attach_id),
$r = q("SELECT id, uid, hash, creator, folder, filename, is_photo, is_dir FROM attach WHERE id IN ( %s ) AND uid = %d",
dbesc($ids_str),
intval($channel_id)
);
......@@ -56,111 +71,153 @@ class Attach_edit extends Controller {
return;
}
$resource = $r[0]['hash'];
$creator = $r[0]['creator'];
$folder = $r[0]['folder'];
$filename = $r[0]['filename'];
$is_photo = intval($r[0]['is_photo']);
$admin_delete = false;
foreach ($r as $rr) {
$is_owner = ((local_channel() == $channel_id) ? true : false);
$is_creator = (($creator == $observer_hash) ? true : false);
$actions_done = '';
$perms = get_all_perms($channel_id, $observer_hash);
$attach_id = $rr['id'];
$resource = $rr['hash'];
$creator = $rr['creator'];
$folder = $rr['folder'];
$filename = $rr['filename'];
$is_photo = intval($rr['is_photo']);
$is_dir = intval($rr['is_dir']);
$admin_delete = false;
if (! ($perms['view_storage'] || is_site_admin())){
notice( t('Permission denied.') . EOL);
return;
}
$is_creator = (($creator == $observer_hash) ? true : false);
$move = ((! $copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
if (! $perms['write_storage']) {
if (is_site_admin()) {
$admin_delete = true;
}
else {
$perms = get_all_perms($channel_id, $observer_hash);
if (! ($perms['view_storage'] || is_site_admin())) {
notice( t('Permission denied.') . EOL);
return;
continue;
}
}
if (!$is_owner && !$admin_delete) {
if(! $is_creator) {
notice( t('Permission denied.') . EOL);
return;
if (! $perms['write_storage']) {
if (is_site_admin()) {
$admin_delete = true;
}
else {
notice( t('Permission denied.') . EOL);
continue;
}
}
}
if ($delete) {
attach_delete($channel_id, $resource, $is_photo);
if (!$is_owner && !$admin_delete) {
if(! $is_creator) {
notice( t('Permission denied.') . EOL);
continue;
}
}
if (! $admin_delete) {
$sync = attach_export_data($channel, $resource, true);
if ($sync) {
Libsync::build_sync_packet($channel_id, ['file' => [$sync]]);
if ($delete) {
attach_delete($channel_id, $resource, $is_photo);
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($attach_id),
intval(TERM_OBJ_FILE)
);
$actions_done .= 'delete,';
}
if ($copy) {
if($is_dir && $resource == $newfolder) {
notice( t('Can not copy folder into itself.') . EOL);
continue;
}
$x = attach_copy($channel_id, $resource, $newfolder, (($single) ? $newfilename : ''));
if ($x['success'])
$resource = $x['resource_id'];
$actions_done .= 'copy,';
}
json_return_and_die([ 'success' => true ]);
}
if ($move) {
if($is_dir && $resource == $newfolder) {
notice( sprintf(t('Can not move folder "%s" into itself.'), $filename) . EOL);
continue;
}
$x = attach_move($channel_id, $resource, $newfolder, (($single) ? $newfilename : ''));
if ($copy) {
$x = attach_copy($channel_id, $resource, $newfolder, $newfilename);
if ($x['success'])
$resource = $x['resource_id'];
}
elseif ($folder !== $newfolder || $filename !== $newfilename) {
$x = attach_move($channel_id, $resource, $newfolder, $newfilename);
}
$actions_done .= 'move,';
if ($categories) {
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($attach_id),
intval(TERM_OBJ_FILE)
);
$cat = explode(',', $categories);
if ($cat) {
foreach($cat as $term) {
$term = trim(escape_tags($term));
if ($term) {
$term_link = z_root() . '/cloud/' . $nick . '/?cat=' . $term;
store_item_tag($channel_id, $attach_id, TERM_OBJ_FILE, TERM_CATEGORY, $term, $term_link);
}
if(! $delete) {
if ($single || (! $single && $categories)) {
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($attach_id),
intval(TERM_OBJ_FILE)
);
$cat = explode(',', $categories);
if ($cat) {
foreach($cat as $term) {
$term = trim(escape_tags($term));
if ($term) {
$term_link = z_root() . '/cloud/' . $nick . '/?cat=' . $term;
store_item_tag($channel_id, $attach_id, TERM_OBJ_FILE, TERM_CATEGORY, $term, $term_link);
}
}
}
$actions_done .= 'cat_add,';
}
else {
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($attach_id),
intval(TERM_OBJ_FILE)
);
$actions_done .= 'cat_remove,';
}
}
}
else {
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
intval($channel_id),
intval($attach_id),
intval(TERM_OBJ_FILE)
);
}
if ($is_owner && ! $dnd) {
$acl = new AccessList($channel);
$acl->set_from_array($_REQUEST);
$x = $acl->get();
if ($is_owner && ($single || (! $single && $permissions))) {
$acl = new AccessList($channel);
$acl->set_from_array($_REQUEST);
$x = $acl->get();
attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true);
attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true);
$actions_done .= 'permissions,';
if ($notify) {
attach_store_item($channel, $observer, $resource);
if ($notify) {
attach_store_item($channel, $observer, $resource);
$actions_done .= 'notify,';
}
}
}
}
$sync = attach_export_data($channel, $resource, false);
if (! $admin_delete && $actions_done) {
$sync = attach_export_data($channel, $resource, false);
if ($sync) {
Libsync::build_sync_packet($channel_id, ['file' => [$sync]]);
}
if ($sync) {
Libsync::build_sync_packet($channel_id, ['file' => [$sync]]);
}
}
if ($dnd) {
json_return_and_die([ 'success' => true ]);
}
$url = get_cloud_url($channel_id, $nick, $resource);
goaway(dirname($url));
logger('attach_edit: ' . $actions_done);
if ($single) {
if($dnd || $delete) {
json_return_and_die([ 'success' => true ]);
}
$url = get_cloud_url($channel_id, $nick, $resource);
goaway(dirname($url));
}
else {
if($dnd || $delete) {
json_return_and_die([ 'success' => true ]);
}
goaway($return_path);
}
}
......
......@@ -103,8 +103,10 @@ class Browser extends DAV\Browser\Plugin {
$parent_path = '';
$siteroot_disabled = get_config('system', 'cloud_disable_siteroot');
// Hide parent folder if in /cloud or category view
if ($channel_id && ! $cat) {
if (($channel_id && ! $cat) || ($siteroot_disabled && $path !== 'cloud')) {
list($parent_uri) = \Sabre\Uri\split($path);
$parent_path = \Sabre\HTTP\encodePath($this->server->getBaseUri() . $parent_uri);
}
......@@ -253,8 +255,8 @@ class Browser extends DAV\Browser\Plugin {
$ft['photo_icon'] = $photo_icon;
$ft['is_owner'] = $is_owner;
$ft['is_creator'] = $is_creator;
$ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$ft['rel_path'] = (($nick) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$ft['full_path'] = z_root() . (($nick) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$ft['name'] = $name;
$ft['type'] = $type;
$ft['size'] = $size;
......@@ -306,12 +308,15 @@ class Browser extends DAV\Browser\Plugin {
$tiles = ((array_key_exists('cloud_tiles',$_SESSION)) ? intval($_SESSION['cloud_tiles']) : $deftiles);
$_SESSION['cloud_tiles'] = $tiles;
if(get_config('system', 'cloud_disable_siteroot') && $parent_path['path'] === '/cloud') {
$parent_path = [];
}
$header = (($cat) ? t('File category') . ": " . $this->escapeHTML($cat) : t('Files') . ": " . $this->escapeHTML($path) . "/");
$channel = channelx_by_n($channel_id);
if($channel) {
$acl = new \Zotlabs\Access\AccessList($channel);
$channel_acl = $acl->get();
$lockstate = (($acl->is_private()) ? 'lock' : 'unlock');
}
$html = replace_macros(get_markup_template('cloud.tpl'), array(
'$header' => $header,
'$total' => t('Total'),
......@@ -340,6 +345,20 @@ class Browser extends DAV\Browser\Plugin {
'$cpdesc' => t('Copy/paste this code to attach file to a post'),
'$cpldesc' => t('Copy/paste this URL to link file from a web page'),
'$categories' => ['categories', t('Categories')],
'$recurse' => ['recurse', t('Set permissions for all files and sub folders'), 0, '', [t('No'), t('Yes')]],
'$newfolder' => ['newfolder', t('Select a target location'), $parent->folder_hash, '', $folder_list],
'$copy' => ['copy', t('Copy to target location'), 0, '', [t('No'), t('Yes')]],
'$return_path' => $path,
'$lockstate' => $lockstate,
'$allow_cid' => acl2json($channel_acl['allow_cid']),
'$allow_gid' => acl2json($channel_acl['allow_gid']),
'$deny_cid' => acl2json($channel_acl['deny_cid']),
'$deny_gid' => acl2json($channel_acl['deny_gid'])
));
$a = false;
......
#files-mkdir-tools,
#files-upload-tools,
.cloud-tool {
.cloud-tool,
.cloud-multi-tool,
#multi-dbtn-acl,
#multi-dropdown-button {
display: none;
}
......@@ -8,7 +11,8 @@
padding: 3px 10px 0px 10px !important;
}
[id^="attach-edit-panel-"] {
[id^="attach-edit-panel-"],
#attach-multi-edit-panel {
padding: 3px 10px 0px 10px !important;
}
......@@ -21,7 +25,7 @@
width: 100%;
}
#cloud-index td:nth-child(2) a {
#cloud-index td:nth-child(3) a {
display: block;
}
......@@ -29,22 +33,26 @@
padding: 7px 3px 7px 10px;
}
#cloud-index td:nth-child(5) {
#cloud-index td:nth-child(6) {
padding: 7px 10px 7px 3px;
}
#cloud-index td:nth-child(2) {
#cloud-index td:nth-child(3) {
word-break: break-all;
}
#cloud-index th:nth-child(6),
#cloud-index td:nth-child(6) {
padding: 7px 3px;
#cloud-index td:nth-child(4) {
white-space: nowrap;
}
#cloud-index th:nth-child(7),
#cloud-index td:nth-child(7) {
padding: 7px 3px;
white-space: nowrap;
}
#cloud-index th:nth-child(8),
#cloud-index td:nth-child(8) {
padding: 7px 10px;
white-space: nowrap;
}
......
......@@ -12,6 +12,8 @@ $(document).ready(function () {
var attach_drop_id;
var attach_draging;
// Per File Tools
$('.cloud-tool-perms-btn').on('click', function (e) {
e.preventDefault();
let id = $(this).data('id');
......@@ -100,6 +102,8 @@ $(document).ready(function () {
});
// Per File Tools Eend
// DnD
$(document).on('drop', function (e) {
......@@ -181,6 +185,125 @@ $(document).ready(function () {
attach_draging = false;
});
// DnD End
// Multi Tools
$('#cloud-multi-tool-select-all').on('change', function (e) {
if ($(this).is(':checked')) {
$('.cloud-multi-tool-checkbox').prop('checked', true);
$('.cloud-index:not(#cloud-index-up)').addClass('cloud-index-selected cloud-index-active');
$('.cloud-tools').addClass('cloud-index-selected');
}
else {
$('.cloud-multi-tool-checkbox').prop('checked', false);
$('.cloud-index').removeClass('cloud-index-selected cloud-index-active');
$('.cloud-tools').removeClass('cloud-index-selected');
}
$('.cloud-multi-tool-checkbox').trigger('change');
});
$('.cloud-multi-tool-checkbox').on('change', function (e) {
let id = $(this).val();
if ($(this).is(':checked')) {
$('#cloud-index-' + id).addClass('cloud-index-selected cloud-index-active');
$('#cloud-tools-' + id).addClass('cloud-index-selected');
$('<input id="aid_' + id + '" class="attach-ids-input" type="hidden" name="attach_ids[]" value="' + id + '">').prependTo('#attach_multi_edit_form');
}
else {
$('#cloud-index-' + id).removeClass('cloud-index-selected cloud-index-active');
$('#cloud-tools-' + id).removeClass('cloud-index-selected');
if ($('#cloud-multi-tool-select-all').is(':checked'))
$('#cloud-multi-tool-select-all').prop('checked', false);
$('#aid_' + id).remove();
}
if($('.cloud-multi-tool-checkbox:checked').length) {
$('#cloud-multi-actions').addClass('cloud-index-active');
$('#multi-dropdown-button').fadeIn();
}
else {
$('#cloud-multi-actions').removeClass('cloud-index-active');
$('#multi-dropdown-button').fadeOut();
}
});
$('#cloud-multi-tool-perms-btn').on('click', function (e) {
e.preventDefault();
$('.cloud-tool, .cloud-multi-tool').hide();
$('#multi-perms').val(1);
$('#cloud-multi-tool-submit, #recurse_container, #multi-dbtn-acl').show();
});
$('#cloud-multi-tool-move-btn').on('click', function (e) {
e.preventDefault();
$('.cloud-tool, .cloud-multi-tool, #recurse_container, #multi-dbtn-acl').hide();
$('#multi-perms').val(0);
$('#cloud-multi-tool-submit, #cloud-multi-tool-move').show();
});
$('#cloud-multi-tool-categories-btn').on('click', function (e) {
e.preventDefault();
$('.cloud-tool, .cloud-multi-tool, #recurse_container, #multi-dbtn-acl').hide();
$('#id_categories').tagsinput({
tagClass: 'badge badge-pill badge-warning text-dark'
});
$('#multi-perms').val(0);
$('#cloud-multi-tool-categories, #cloud-multi-tool-submit').show();
});
$('#cloud-multi-tool-delete-btn').on('click', function (e) {
e.preventDefault();
let post_data = $('.cloud-multi-tool-checkbox').serializeArray();
if(! post_data.length) {
return false;
}
let confirm = confirmDelete();
if (confirm) {
$('body').css('cursor', 'wait');
$('.cloud-index-selected').css('opacity', 0.33);
post_data.push(
{ name: 'channel_id', value: channelId },
{ name: 'delete', value: 1},
);
$.post('attach_edit', post_data, function (data) {
if (data.success) {
console.log(data);
$('.cloud-index-selected').remove();
$('body').css('cursor', 'auto');
}
return true;
});
}
return false;
});
$('.cloud-multi-tool-cancel-btn').on('click', function (e) {
e.preventDefault();
$('.cloud-multi-tool, #recurse_container, #multi-dbtn-acl').hide();
$('#attach_multi_edit_form').trigger('reset');
$('#multi-perms').val(0);
$('#id_categories').tagsinput('destroy');
});
// Multi Tools End
});
......@@ -258,6 +381,7 @@ function prepareHtml(f) {
var i = f.count;
$('#cloud-index #new-upload-progress-bar-' + num.toString()).after(
'<tr id="new-upload-' + i + '" class="new-upload">' +
'<td></td>' +
'<td><i class="fa ' + getIconFromType(f.type) + '" title="' + f.type + '"></i></td>' +
'<td>' + f.name + '</td>' +
'<td id="upload-progress-' + i + '"></td><td></td><td></td>' +
......
......@@ -45,8 +45,9 @@
<table id="cloud-index">
<tr>
<th width="1%">{{* multi tool checkbox *}}</th>
<th width="1%">{{* icon *}}</th>
<th width="94%">{{$name}}</th>
<th width="93%">{{$name}}</th>
<th width="1%">{{* categories *}}</th>
<th width="1%">{{* lock icon *}}</th>
<th width="1%">{{* tools icon *}}</th>
......@@ -55,13 +56,95 @@
</tr>
{{if $parentpath}}
<tr id="cloud-index-up" class="cloud-index{{if ! $is_root_folder}} attach-drop{{/if}}"{{if ! $is_root_folder}} data-folder="{{$folder_parent}}"/{{/if}}>
<td></td>
<td><i class="fa fa-level-up"></i></td>
<td colspan="7"><a href="{{$parentpath}}" title="{{$parent}}" draggable="false">..</a></td>
<td colspan="6"><a href="{{$parentpath}}" title="{{$parent}}" draggable="false">..</a></td>
</tr>
{{/if}}
{{if $entries.0}}
<tr id="cloud-multi-actions">
<td colspan="2">
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="cloud-multi-tool-select-all" value="" title="Select all">
</div>
</td>
<td colspan="3">
<div class="form-check form-check-inline">
<label class="form-check-label" for="cloud-multi-tool-select-all">Select all</label>
</div>
</td>
<td colspan="3">