* Add preference to set the filename of merged documents using placeholders

This commit is contained in:
nathan 2021-10-06 11:58:39 -06:00
parent 5b3a6c02b4
commit 45f039da95
5 changed files with 179 additions and 64 deletions

View File

@ -304,17 +304,27 @@ class addressbook_hooks
'admin' => False,
);
$settings['document_dir'] = array(
'type' => 'vfs_dirs',
'size' => 60,
'label' => 'Directory with documents to insert contacts',
'name' => 'document_dir',
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the data inserted.',lang('addressbook')).' '.
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','n_fn').' '.
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
'type' => 'vfs_dirs',
'size' => 60,
'label' => 'Directory with documents to insert contacts',
'name' => 'document_dir',
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the data inserted.', lang('addressbook')) . ' ' .
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'n_fn') . ' ' .
lang('The following document-types are supported:') . implode(',', Api\Storage\Merge::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/addressbook',
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/addressbook',
);
$settings[Api\Storage\Merge::PREF_DOCUMENT_FILENAME] = array(
'type' => 'taglist',
'label' => 'Document download filename',
'name' => 'document_download_name',
'values' => Api\Storage\Merge::DOCUMENT_FILENAME_OPTIONS,
'help' => 'Choose the default filename for downloaded documents.',
'xmlrpc' => True,
'admin' => False,
'default' => 'document',
);
}

View File

@ -32,9 +32,26 @@ use ZipArchive;
*/
abstract class Merge
{
/**
* Preference, path where we will put the generated document
*/
const PREF_STORE_LOCATION = "merge_store_path";
/**
* Preference, placeholders for creating the filename of the generated document
*/
const PREF_DOCUMENT_FILENAME = "document_download_name";
/**
* List of placeholders
*/
const DOCUMENT_FILENAME_OPTIONS = [
'$$document$$' => 'Template name',
'$$link_title$$' => 'Entry link-title',
'$$contact_title$$' => 'Contact link-title',
'$$current_date$$' => 'Current date',
];
/**
* Instance of the addressbook_bo class
*
@ -246,47 +263,59 @@ abstract class Merge
$replacements = array();
foreach(array_keys($this->contacts->contact_fields) as $name)
{
$value = $contact[$name] ?? null;
$value = $contact[$name] ?? '';
if(!$value)
{
continue;
}
switch($name)
{
case 'created': case 'modified':
if($value) $value = Api\DateTime::to($value);
case 'created':
case 'modified':
if($value)
{
$value = Api\DateTime::to($value);
}
break;
case 'bday':
if ($value)
if($value)
{
try {
try
{
$value = Api\DateTime::to($value, true);
}
catch (\Exception $e) {
unset($e); // ignore exception caused by wrongly formatted date
catch (\Exception $e)
{
unset($e); // ignore exception caused by wrongly formatted date
}
}
break;
case 'owner': case 'creator': case 'modifier':
case 'owner':
case 'creator':
case 'modifier':
$value = Api\Accounts::username($value);
break;
case 'cat_id':
if ($value)
if($value)
{
// if cat-tree is displayed, we return a full category path not just the name of the cat
$use = $GLOBALS['egw_info']['server']['cat_tab'] == 'Tree' ? 'path' : 'name';
$cats = array();
foreach(is_array($value) ? $value : explode(',',$value) as $cat_id)
foreach(is_array($value) ? $value : explode(',', $value) as $cat_id)
{
$cats[] = $GLOBALS['egw']->categories->id2name($cat_id,$use);
$cats[] = $GLOBALS['egw']->categories->id2name($cat_id, $use);
}
$value = implode(', ',$cats);
$value = implode(', ', $cats);
}
break;
case 'jpegphoto': // returning a link might make more sense then the binary photo
if ($contact['photo'])
case 'jpegphoto': // returning a link might make more sense then the binary photo
if($contact['photo'])
{
$value = Api\Framework::getUrl(Api\Framework::link('/index.php',$contact['photo']));
$value = Api\Framework::getUrl(Api\Framework::link('/index.php', $contact['photo']));
}
break;
case 'tel_prefer':
if ($value && $contact[$value])
if($value && $contact[$value])
{
$value = $contact[$value];
}
@ -1615,7 +1644,7 @@ abstract class Merge
public static function get_app_class($appname)
{
$classname = "{$appname}_merge";
if(class_exists($classname) && is_subclass_of($classname, 'EGroupware\\Api\\Storage\\Merge'))
if(class_exists($classname, false) && is_subclass_of($classname, 'EGroupware\\Api\\Storage\\Merge'))
{
$document_merge = new $classname();
}
@ -1644,14 +1673,9 @@ abstract class Merge
try
{
$classname = "{$app}_merge";
if(!class_exists($classname))
{
return $replacements;
}
$class = new $classname();
$method = $app.'_replacements';
if(method_exists($class,$method))
$class = $this->get_app_class($app);
$method = $app . '_replacements';
if(method_exists($class, $method))
{
$replacements = $class->$method($id, $prefix, $content);
}
@ -2447,7 +2471,7 @@ abstract class Merge
$pdf = (boolean)$_REQUEST['pdf'];
}
$filename = $document_merge->get_filename($_REQUEST['document']);
$filename = $document_merge->get_filename($_REQUEST['document'], $ids);
$result = $document_merge->merge_file($_REQUEST['document'], $ids, $filename, '', $header);
if(!is_file($result) || !is_readable($result))
@ -2519,12 +2543,54 @@ abstract class Merge
/**
* Generate a filename for the merged file, without extension
*
* Default is just the name of the template
* Default filename is just the name of the template.
* We use the placeholders from get_filename_placeholders() and the application's document filename preference
* to generate a custom filename.
*
* @param string $document Template filename
* @param string[] $ids List of IDs being merged
* @return string
*/
protected function get_filename($document) : string
protected function get_filename($document, $ids = []) : string
{
return '';
$name = '';
if(isset($GLOBALS['egw_info']['user']['preferences'][$this->get_app()][static::PREF_DOCUMENT_FILENAME]))
{
$pref = $GLOBALS['egw_info']['user']['preferences'][$this->get_app()][static::PREF_DOCUMENT_FILENAME];
$placeholders = $this->get_filename_placeholders($document, $ids);
// Make values safe for VFS
foreach($placeholders as &$value)
{
$value = Api\Mail::clean_subject_for_filename($value);
}
// Do replacement
$name = str_replace(
array_keys($placeholders),
array_values($placeholders),
is_array($pref) ? implode(' ', $pref) : $pref
);
}
return $name;
}
protected function get_filename_placeholders($document, $ids)
{
$ext = '.' . pathinfo($document, PATHINFO_EXTENSION);
$link_title = count($ids) == 1 ? Api\Link::title($this->get_app(), $ids[0]) : lang("multiple");
$contact_title = count($ids) == 1 ? Api\Link::title($this->get_app(), $ids[0]) : lang("multiple");
$current_date = str_replace('/', '-', Api\DateTime::to('now', Api\DateTime::$user_dateformat));
$values = [
'$$document$$' => basename($document, $ext),
'$$link_title$$' => $link_title,
'$$contact_title$$' => $contact_title,
'$$current_date$$' => $current_date
];
return $values;
}
/**

View File

@ -471,17 +471,27 @@ class infolog_hooks
'admin' => False,
);
$settings['document_dir'] = array(
'type' => 'vfs_dirs',
'size' => 60,
'label' => 'Directory with documents to insert entries',
'name' => 'document_dir',
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the data inserted.',lang('infolog')).' '.
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','info_subject').' '.
lang('The following document-types are supported:').'*.rtf, *.txt',
'type' => 'vfs_dirs',
'size' => 60,
'label' => 'Directory with documents to insert entries',
'name' => 'document_dir',
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the data inserted.', lang('infolog')) . ' ' .
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'info_subject') . ' ' .
lang('The following document-types are supported:') . '*.rtf, *.txt',
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/infolog',
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/infolog',
);
$settings[Api\Storage\Merge::PREF_DOCUMENT_FILENAME] = array(
'type' => 'taglist',
'label' => 'Document download filename',
'name' => 'document_download_name',
'values' => Api\Storage\Merge::DOCUMENT_FILENAME_OPTIONS,
'help' => 'Choose the default filename for downloaded documents.',
'xmlrpc' => True,
'admin' => False,
'default' => 'document',
);
}

View File

@ -64,23 +64,42 @@ class infolog_merge extends Api\Storage\Merge
* @param string &$content=null content to create some replacements only if they are use
* @return array|boolean
*/
protected function get_replacements($id,&$content=null)
protected function get_replacements($id, &$content = null)
{
if (!($replacements = $this->infolog_replacements($id, '', $content)))
if(!($replacements = $this->infolog_replacements($id, '', $content)))
{
return false;
}
return $replacements;
}
/**
* Override contact filename placeholder to use info_contact
*
* @param $document
* @param $ids
* @return array|void
*/
public function get_filename_placeholders($document, $ids)
{
$placeholders = parent::get_filename_placeholders($document, $ids);
if(count($ids) == 1 && ($info = $this->bo->read($ids[0])))
{
$placeholders['$$contact_title$$'] = $info['info_contact']['title'] ??
(is_array($info['info_contact']) && Link::title($info['info_contact']['app'], $info['info_contact']['id'])) ??
'';
}
return $placeholders;
}
/**
* Get infolog replacements
*
* @param int $id id of entry
* @param string $prefix='' prefix like eg. 'erole'
* @param string $prefix ='' prefix like eg. 'erole'
* @return array|boolean
*/
public function infolog_replacements($id,$prefix='', &$content = '')
public function infolog_replacements($id, $prefix = '', &$content = '')
{
$record = new infolog_egw_record($id);
$info = array();

View File

@ -191,17 +191,27 @@ class timesheet_hooks
'admin' => False,
);
$settings['document_dir'] = array(
'type' => 'vfs_dirs',
'size' => 60,
'label' => 'Directory with documents to insert entries',
'name' => 'document_dir',
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the %1 data inserted.', lang('timesheet')).' '.
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','ts_title').' '.
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
'type' => 'vfs_dirs',
'size' => 60,
'label' => 'Directory with documents to insert entries',
'name' => 'document_dir',
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the %1 data inserted.', lang('timesheet')) . ' ' .
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'ts_title') . ' ' .
lang('The following document-types are supported:') . implode(',', Api\Storage\Merge::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/timesheet',
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/timesheet',
);
$settings[Api\Storage\Merge::PREF_DOCUMENT_FILENAME] = array(
'type' => 'taglist',
'label' => 'Document download filename',
'name' => 'document_download_name',
'values' => Api\Storage\Merge::DOCUMENT_FILENAME_OPTIONS,
'help' => 'Choose the default filename for downloaded documents.',
'xmlrpc' => True,
'admin' => False,
'default' => 'document',
);
}