From a728276a151cb6afd42a430b917a334f06208cb5 Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 23 Jun 2021 11:19:36 -0600 Subject: [PATCH] Change merge download/Collabora to a common generation method, avoiding need to POST. This allows Kanban to download merge files --- addressbook/js/app.ts | 19 +- .../et2_extension_nextmatch_actions.js | 5 + api/src/Contacts/Merge.php | 1 + api/src/Storage/Merge.php | 208 ++++++++++++++++-- 4 files changed, 203 insertions(+), 30 deletions(-) diff --git a/addressbook/js/app.ts b/addressbook/js/app.ts index 9533d0cf6f..827d646978 100644 --- a/addressbook/js/app.ts +++ b/addressbook/js/app.ts @@ -3,8 +3,9 @@ * * @link: https://www.egroupware.org * @package addressbook - * @author Hadi Nategh - * @copyright (c) 2008-13 by Ralf Becker + * @author Hadi Nategh + * @author Ralf Becker + * @copyright (c) 2008-21 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License */ @@ -12,13 +13,13 @@ /api/js/jsapi/egw_app.js */ -import 'jquery'; -import 'jqueryui'; -import '../jsapi/egw_global'; -import '../etemplate/et2_types'; - import {EgwApp, PushData} from '../../api/js/jsapi/egw_app'; import {etemplate2} from "../../api/js/etemplate/etemplate2"; +import {et2_dialog} from "../../api/js/etemplate/et2_widget_dialog"; +import {et2_selectbox} from "../../api/js/etemplate/et2_widget_selectbox"; +import {nm_action, fetchAll} from "../../api/js/etemplate/et2_extension_nextmatch_actions.js"; +import "./CRM.js"; +import {egw} from "../../api/js/jsapi/egw_global"; /** * Object to call app.addressbook.openCRMview with @@ -695,11 +696,11 @@ class AddressbookApp extends EgwApp { content.push({"confirm":id,"name":_data.doublicates[id]}); } - confirmdialog(this.egw.lang('Duplicate warning'),content,et2_dialog.BUTTONs_OK_CANCEL); + confirmdialog(this.egw.lang('Duplicate warning'),content,et2_dialog.BUTTONS_OK_CANCEL); } if (typeof _data.fileas_options == 'object' && this.et2) { - var selbox = this.et2.getWidgetById('fileas_type'); + var selbox = this.et2.getWidgetById('fileas_type'); if (selbox) { selbox.set_select_options(_data.fileas_sel_options); diff --git a/api/js/etemplate/et2_extension_nextmatch_actions.js b/api/js/etemplate/et2_extension_nextmatch_actions.js index d059122a3c..f6337994d7 100644 --- a/api/js/etemplate/et2_extension_nextmatch_actions.js +++ b/api/js/etemplate/et2_extension_nextmatch_actions.js @@ -46,6 +46,11 @@ function nm_action(_action, _senders, _target, _ids) _ids = nm.getSelection(); _action.data.nextmatch = nm; } + else + { + // This will probably fail without nm, but it depends on the action + _ids = {ids: _senders.map(function(s) {return s.id;})}; + } } // row ids var row_ids = ""; diff --git a/api/src/Contacts/Merge.php b/api/src/Contacts/Merge.php index c1240cbfee..796e767135 100644 --- a/api/src/Contacts/Merge.php +++ b/api/src/Contacts/Merge.php @@ -31,6 +31,7 @@ class Merge extends Api\Storage\Merge var $public_functions = array( 'download_by_request' => true, 'show_replacements' => true, + "merge_entries" => true ); /** diff --git a/api/src/Storage/Merge.php b/api/src/Storage/Merge.php index b5ab67ce3a..26883d552b 100644 --- a/api/src/Storage/Merge.php +++ b/api/src/Storage/Merge.php @@ -15,6 +15,7 @@ namespace EGroupware\Api\Storage; use DOMDocument; use EGroupware\Api; +use EGroupware\Api\Vfs; use EGroupware\Stylite; use tidy; use uiaccountsel; @@ -77,6 +78,9 @@ abstract class Merge public $export_limit; + public $public_functions = array( + "merge_entries" => true + ); /** * Configuration for HTML Tidy to clean up any HTML content that is kept */ @@ -2083,10 +2087,6 @@ abstract class Merge { self::document_mail_action($documents['document'], $file); } - else if ($editable_mimes[$file['mime']]) - { - self::document_editable_action($documents['document'], $file); - } } $files = array(); @@ -2164,10 +2164,6 @@ abstract class Merge { self::document_mail_action($current_level[$prefix.$file['name']], $file); } - else if ($editable_mimes[$file['mime']]) - { - self::document_editable_action($current_level[$prefix.$file['name']], $file); - } break; default: @@ -2198,16 +2194,21 @@ abstract class Merge } $documents[$file['mime']]['children'][$prefix.$file['name']] = array( 'caption' => Api\Vfs::decodePath($file['name']), + 'target' => '_blank', 'postSubmit' => true, // download needs post submit (not Ajax) to work ); + $edit_attributes = array( + 'menuaction' => $GLOBALS['egw_info']['flags']['currentapp'].'.'.get_called_class().'.merge_entries', + 'document' => $file['path'], + 'merge' => get_called_class(), + 'id' => '$id', + 'select_all' => '$select_all' + ); + $documents[$file['mime']]['children'][$prefix.$file['name']]['url'] = urldecode(http_build_query($edit_attributes)); if ($file['mime'] == 'message/rfc822') { self::document_mail_action($documents[$file['mime']]['children'][$prefix.$file['name']], $file); } - else if ($editable_mimes[$file['mime']]) - { - self::document_editable_action($documents[$file['mime']]['children'][$prefix.$file['name']], $file); - } } else { @@ -2215,16 +2216,21 @@ abstract class Merge 'icon' => Api\Vfs::mime_icon($file['mime']), 'caption' => Api\Vfs::decodePath($file['name']), 'group' => 2, - 'postSubmit' => true, // download needs post submit (not Ajax) to work + 'target' => '_blank' ); + + $edit_attributes = array( + 'menuaction' => $GLOBALS['egw_info']['flags']['currentapp'].'.'.get_called_class().'.merge_entries', + 'document' => $file['path'], + 'merge' => get_called_class(), + 'id' => '$id', + 'select_all' => '$select_all' + ); + $documents[$prefix.$file['name']]['url'] = urldecode(http_build_query($edit_attributes)); if ($file['mime'] == 'message/rfc822') { self::document_mail_action($documents[$prefix.$file['name']], $file); } - else if ($editable_mimes[$file['mime']]) - { - self::document_editable_action($documents[$prefix.$file['name']], $file); - } } } @@ -2286,15 +2292,14 @@ abstract class Merge private static function document_editable_action(Array &$action, $file) { unset($action['postSubmit']); - $action['nm_action'] = 'location'; - $action['url'] = urldecode(http_build_query(array( + $edit_attributes = array( 'menuaction' => 'collabora.EGroupware\\collabora\\Ui.merge_edit', 'document' => $file['path'], 'merge' => get_called_class(), 'id' => '$id', 'select_all' => '$select_all' - ))); - $action['target'] = '_blank'; + ); + $action['url'] = urldecode(http_build_query($edit_attributes)); } /** @@ -2331,6 +2336,167 @@ abstract class Merge return lang("Document '%1' does not exist or is not readable for you!",$document); } + /** + * Merge the selected IDs into the given document, save it to the VFS, then + * either open it in the editor or have the browser download the file. + */ + public static function merge_entries() + { + if (class_exists($_REQUEST['merge']) && is_subclass_of($_REQUEST['merge'], 'EGroupware\\Api\\Storage\\Merge')) + { + $document_merge = new $_REQUEST['merge'](); + } + else + { + $document_merge = new Api\Contacts\Merge(); + } + + if(($error = $document_merge->check_document($_REQUEST['document'],''))) + { + $response->error($error); + return; + } + + $ids = is_string($_REQUEST['id']) && strpos($_REQUEST['id'],'[') === FALSE ? explode(',',$_REQUEST['id']) : json_decode($_REQUEST['id'],true); + if($_REQUEST['select_all'] === 'true') + { + $ids = self::get_all_ids($document_merge); + } + + $filename = ''; + $result = $document_merge->merge_file($_REQUEST['document'], $ids, $filename, '', $header); + + if(!is_file($result) || !is_readable($result)) + { + throw new Api\Exception\AssertionFailed("Unable to generate merge file\n". $result); + } + // Put it into the vfs using user's configured home dir if writable, + // or expected home dir (/home/username) if not + $target = $_target = (Vfs::is_writable(Vfs::get_home_dir()) ? + Vfs::get_home_dir() : + "/home/{$GLOBALS['egw_info']['user']['account_lid']}" + )."/$filename"; + $dupe_count = 0; + while(is_file(Vfs::PREFIX.$target)) + { + $dupe_count++; + $target = Vfs::dirname($_target) . '/' . + pathinfo($filename, PATHINFO_FILENAME) . + ' ('.($dupe_count + 1).')' . '.' . + pathinfo($filename, PATHINFO_EXTENSION); + } + copy($result, Vfs::PREFIX.$target); + unlink($result); + + // Find out what to do with it + $editable_mimes = array(); + try { + if (class_exists('EGroupware\\collabora\\Bo') && + $GLOBALS['egw_info']['user']['apps']['collabora'] && + ($discovery = \EGroupware\collabora\Bo::discover()) && + $GLOBALS['egw_info']['user']['preferences']['filemanager']['merge_open_handler'] != 'download' + ) + { + $editable_mimes = $discovery; + } + } + catch (\Exception $e) + { + // ignore failed discovery + unset($e); + } + if($editable_mimes[Vfs::mime_content_type($target)]) + { + \Egroupware\Api\Egw::redirect_link('/index.php', array( + 'menuaction' => 'collabora.EGroupware\\Collabora\\Ui.editor', + 'path'=> $target + )); + } + else + { + \Egroupware\Api\Egw::redirect_link(Vfs::download_url($target)); + } + } + + /** + * Get all ids for when they try to do 'Select All', then merge into document + * + * @param Api\Contacts\Merge $merge App-specific merge object + */ + protected function get_all_ids(Api\Storage\Merge $merge) + { + $ids = array(); + $locations = array('index', 'session_data'); + + // Get app + list($appname, $_merge) = explode('_', get_class($merge)); + + if($merge instanceOf Api\Contacts\Merge) + { + $appname = 'addressbook'; + } + switch(get_class($merge)) + { + case \calendar_merge::class: + $ui_class = 'calendar_uilist'; + $locations = array('calendar_list'); + break; + case \projectmanager_merge::class; + $ui_class = 'projectmanager_ui'; + $locations = array('project_list'); + break; + default: + $ui_class = $appname . '_ui'; + break; + } + + // Ask app + if(class_exists($ui_class)) + { + $ui = new $ui_class(); + if( method_exists($ui_class, 'get_all_ids') ) + { + return $ui->get_all_ids(); + } + + // Try cache + if( method_exists($ui_class, 'get_rows')) + { + foreach($locations as $location) + { + $session = Api\Cache::getSession($appname, $location); + if($session && $session['row_id']) + { + break; + } + } + $rows = $readonlys = array(); + @set_time_limit(0); // switch off the execution time limit, as it's for big selections to small + $session['num_rows'] = -1; // all + $ui->get_rows($session, $rows, $readonlys); + foreach($rows as $row_number => $row) + { + if(!is_numeric($row_number)) continue; + $row_id = $row[$session['row_id'] ? $session['row_id'] : 'id']; + switch (get_class($merge)) + { + case \calendar_merge::class: + $explody = explode(':',$row_id); + $ids[] = array('id' => $explody[0], 'recur_date' => $explody[1]); + break; + case \timesheet_merge::class: + // Skip the rows with totalss + if(!is_numeric($row_id)) continue 2; // +1 for switch + // Fall through + default: + $ids[] = $row_id; + } + } + } + } + return $ids; + } + /** * Get a list of supported extentions */