- Add generation target path preference to all apps

- Use common method to get merge preferences to reduce duplication
This commit is contained in:
nathan 2021-10-07 10:22:45 -06:00
parent 40af04e38e
commit da6a16e62d
6 changed files with 884 additions and 752 deletions

View File

@ -291,41 +291,8 @@ class addressbook_hooks
if ($GLOBALS['egw_info']['user']['apps']['filemanager']) if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
{ {
$settings['default_document'] = array( $merge = new Api\Contacts\Merge();
'type' => 'vfs_file', $settings += $merge->merge_preferences();
'size' => 60,
'label' => 'Default document to insert contacts',
'name' => 'default_document',
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon 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,
);
$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()),
'run_lang' => false,
'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',
);
} }
if ($GLOBALS['egw_info']['user']['apps']['felamimail'] || $GLOBALS['egw_info']['user']['apps']['mail']) if ($GLOBALS['egw_info']['user']['apps']['felamimail'] || $GLOBALS['egw_info']['user']['apps']['mail'])

View File

@ -32,6 +32,16 @@ use ZipArchive;
*/ */
abstract class Merge abstract class Merge
{ {
/**
* Preference, path where we look for merge templates
*/
public const PREF_TEMPLATE_DIR = 'document_dir';
/**
* Preference, path to special documents that are listed first
*/
public const PREF_DEFAULT_TEMPLATE = 'default_document';
/** /**
* Preference, path where we will put the generated document * Preference, path where we will put the generated document
*/ */
@ -205,7 +215,10 @@ abstract class Merge
switch($mimetype) switch($mimetype)
{ {
case 'application/msword': case 'application/msword':
if (strtolower($extension) != '.rtf') break; if(strtolower($extension) != '.rtf')
{
break;
}
case 'application/rtf': case 'application/rtf':
case 'text/rtf': case 'text/rtf':
return true; // rtf files return true; // rtf files
@ -215,7 +228,10 @@ abstract class Merge
case 'application/vnd.oasis.opendocument.text-template': case 'application/vnd.oasis.opendocument.text-template':
case 'application/vnd.oasis.opendocument.spreadsheet-template': case 'application/vnd.oasis.opendocument.spreadsheet-template':
case 'application/vnd.oasis.opendocument.presentation-template': case 'application/vnd.oasis.opendocument.presentation-template':
if (!$zip_available) break; if(!$zip_available)
{
break;
}
return true; // open office write xml files return true; // open office write xml files
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': // ms word 2007 xml format case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': // ms word 2007 xml format
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.d': // mimetypes in vfs are limited to 64 chars case 'application/vnd.openxmlformats-officedocument.wordprocessingml.d': // mimetypes in vfs are limited to 64 chars
@ -223,7 +239,10 @@ abstract class Merge
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': // ms excel 2007 xml format case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': // ms excel 2007 xml format
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.shee': case 'application/vnd.openxmlformats-officedocument.spreadsheetml.shee':
case 'application/vnd.ms-excel.sheet.macroenabled.12': case 'application/vnd.ms-excel.sheet.macroenabled.12':
if (!$zip_available) break; if(!$zip_available)
{
break;
}
return true; // ms word xml format return true; // ms word xml format
case 'application/xml': case 'application/xml':
return true; // alias for text/xml, eg. ms office 2003 word format return true; // alias for text/xml, eg. ms office 2003 word format
@ -258,7 +277,10 @@ abstract class Merge
{ {
$contact = $this->contacts->read($contact, $ignore_acl); $contact = $this->contacts->read($contact, $ignore_acl);
} }
if (!is_array($contact)) return array(); if(!is_array($contact))
{
return array();
}
$replacements = array(); $replacements = array();
foreach(array_keys($this->contacts->contact_fields) as $name) foreach(array_keys($this->contacts->contact_fields) as $name)
@ -382,13 +404,18 @@ abstract class Merge
$cats = array(); $cats = array();
foreach(is_array($contact['cat_id']) ? $contact['cat_id'] : explode(',', $contact['cat_id']) as $cat_id) foreach(is_array($contact['cat_id']) ? $contact['cat_id'] : explode(',', $contact['cat_id']) as $cat_id)
{ {
if(!$cat_id) continue; if(!$cat_id)
{
continue;
}
if($GLOBALS['egw']->categories->id2name($cat_id, 'main') != $cat_id) if($GLOBALS['egw']->categories->id2name($cat_id, 'main') != $cat_id)
{ {
$path = explode(' / ', $GLOBALS['egw']->categories->id2name($cat_id, 'path')); $path = explode(' / ', $GLOBALS['egw']->categories->id2name($cat_id, 'path'));
unset($path[0]); // Drop main unset($path[0]); // Drop main
$cats[$GLOBALS['egw']->categories->id2name($cat_id, 'main')][] = implode(' / ', $path); $cats[$GLOBALS['egw']->categories->id2name($cat_id, 'main')][] = implode(' / ', $path);
} elseif($cat_id) { }
elseif($cat_id)
{
$cats[$cat_id] = array(); $cats[$cat_id] = array();
} }
} }
@ -429,7 +456,10 @@ abstract class Merge
'id' => $link_info 'id' => $link_info
); );
} }
if($exclude && in_array($link_info['id'], $exclude)) continue; if($exclude && in_array($link_info['id'], $exclude))
{
continue;
}
$title = Api\Link::title($link_info['app'], $link_info['id']); $title = Api\Link::title($link_info['app'], $link_info['id']);
@ -446,7 +476,10 @@ abstract class Merge
$link = Api\Framework::link($link, array()); $link = Api\Framework::link($link, array());
} }
// Prepend site // Prepend site
if ($link[0] == '/') $link = Api\Framework::getUrl($link); if($link[0] == '/')
{
$link = Api\Framework::getUrl($link);
}
$title = $style == 'href' ? Api\Html::a_href(Api\Html::htmlspecialchars($title), $link) : $link; $title = $style == 'href' ? Api\Html::a_href(Api\Html::htmlspecialchars($title), $link) : $link;
} }
@ -502,19 +535,25 @@ abstract class Merge
$link = Api\Framework::link($link, array()); $link = Api\Framework::link($link, array());
} }
// Prepend site // Prepend site
if ($link[0] == '/') $link = Api\Framework::getUrl($link); if($link[0] == '/')
{
$link = Api\Framework::getUrl($link);
}
// Formatting // Formatting
if($matches[2][$i] == 'title') if($matches[2][$i] == 'title')
{ {
$link = $title; $link = $title;
} }
else if($matches[2][$i] == 'href') else
{
if($matches[2][$i] == 'href')
{ {
// Turn on HTML style parsing or the link will be escaped // Turn on HTML style parsing or the link will be escaped
$this->parse_html_styles = true; $this->parse_html_styles = true;
$link = Api\Html::a_href(Api\Html::htmlspecialchars($title), $link); $link = Api\Html::a_href(Api\Html::htmlspecialchars($title), $link);
} }
}
$array['$$' . ($prefix ? $prefix . '/' : '') . $placeholder . '$$'] = $link; $array['$$' . ($prefix ? $prefix . '/' : '') . $placeholder . '$$'] = $link;
break; break;
@ -589,7 +628,8 @@ abstract class Merge
// Need to create the share here. // Need to create the share here.
// No way to know here if it should be writable, or who it's going to // No way to know here if it should be writable, or who it's going to
$mode = /* ? ? Sharing::WRITABLE :*/ Api\Sharing::READONLY; $mode = /* ? ? Sharing::WRITABLE :*/
Api\Sharing::READONLY;
$recipients = array(); $recipients = array();
$extra = array(); $extra = array();
@ -603,13 +643,16 @@ abstract class Merge
* *
* @param int|string|DateTime $time unix timestamp or Y-m-d H:i:s string (in user time!) * @param int|string|DateTime $time unix timestamp or Y-m-d H:i:s string (in user time!)
* @param string $format =null format string, default $this->datetime_format * @param string $format =null format string, default $this->datetime_format
* @deprecated use Api\DateTime::to($time='now',$format='')
* @return string * @return string
* @deprecated use Api\DateTime::to($time='now',$format='')
*/ */
protected function format_datetime($time, $format = null) protected function format_datetime($time, $format = null)
{ {
trigger_error(__METHOD__ . ' is deprecated, use Api\DateTime::to($time, $format)', E_USER_DEPRECATED); trigger_error(__METHOD__ . ' is deprecated, use Api\DateTime::to($time, $format)', E_USER_DEPRECATED);
if (is_null($format)) $format = $this->datetime_format; if(is_null($format))
{
$format = $this->datetime_format;
}
return Api\DateTime::to($time, $format); return Api\DateTime::to($time, $format);
} }
@ -651,7 +694,10 @@ abstract class Merge
public static function getExportLimit($app = 'common') public static function getExportLimit($app = 'common')
{ {
static $exportLimitStore = array(); static $exportLimitStore = array();
if (empty($app)) $app='common'; if(empty($app))
{
$app = 'common';
}
//error_log(__METHOD__.__LINE__.' called with app:'.$app); //error_log(__METHOD__.__LINE__.' called with app:'.$app);
if(!array_key_exists($app, $exportLimitStore)) if(!array_key_exists($app, $exportLimitStore))
{ {
@ -660,7 +706,10 @@ abstract class Merge
if($app != 'common') if($app != 'common')
{ {
$app_limit = Api\Hooks::single('export_limit', $app); $app_limit = Api\Hooks::single('export_limit', $app);
if ($app_limit) $exportLimitStore[$app] = $app_limit; if($app_limit)
{
$exportLimitStore[$app] = $app_limit;
}
} }
//error_log(__METHOD__.__LINE__.' building cache for app:'.$app.' -> '.$exportLimitStore[$app]); //error_log(__METHOD__.__LINE__.' building cache for app:'.$app.' -> '.$exportLimitStore[$app]);
if(empty($exportLimitStore[$app])) if(empty($exportLimitStore[$app]))
@ -693,10 +742,22 @@ abstract class Merge
*/ */
public static function hasExportLimit($app_limit, $checkas = 'AND') public static function hasExportLimit($app_limit, $checkas = 'AND')
{ {
if (strtoupper($checkas) == 'ISALLOWED') return (empty($app_limit) || ($app_limit !='no' && $app_limit > 0) ); if(strtoupper($checkas) == 'ISALLOWED')
if (empty($app_limit)) return false; {
if ($app_limit == 'no') return true; return (empty($app_limit) || ($app_limit != 'no' && $app_limit > 0));
if ($app_limit > 0) return true; }
if(empty($app_limit))
{
return false;
}
if($app_limit == 'no')
{
return true;
}
if($app_limit > 0)
{
return true;
}
} }
/** /**
@ -731,9 +792,12 @@ abstract class Merge
$mimetype = 'application/rtf'; $mimetype = 'application/rtf';
} }
try { try
{
$content = $this->merge_string($content, $ids, $err, $mimetype, $fix); $content = $this->merge_string($content, $ids, $err, $mimetype, $fix);
} catch (\Exception $e) { }
catch (\Exception $e)
{
_egw_log_exception($e); _egw_log_exception($e);
$err = $e->getMessage(); $err = $e->getMessage();
$ret = false; $ret = false;
@ -798,7 +862,8 @@ abstract class Merge
{ {
$content = preg_replace('/<span>(.*?)<\/span>/', '$1', $content, -1, $count); $content = preg_replace('/<span>(.*?)<\/span>/', '$1', $content, -1, $count);
$i++; $i++;
} while($count > 0 && $i < 10); }
while($count > 0 && $i < 10);
$doc = new DOMDocument(); $doc = new DOMDocument();
$xslt = new XSLTProcessor(); $xslt = new XSLTProcessor();
@ -822,7 +887,8 @@ abstract class Merge
$content = $xslt->transformToXml($element); $content = $xslt->transformToXml($element);
//echo $content;die(); //echo $content;die();
// Word 2003 needs two declarations, add extra declaration back in // Word 2003 needs two declarations, add extra declaration back in
if($mimetype == 'application/xml' && $mso_application_progid == 'Word.Document' && strpos($content, '<?xml') !== 0) { if($mimetype == 'application/xml' && $mso_application_progid == 'Word.Document' && strpos($content, '<?xml') !== 0)
{
$content = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . $content; $content = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . $content;
} }
// Validate // Validate
@ -879,13 +945,18 @@ abstract class Merge
$content = preg_replace(array_keys($fix), array_values($fix), $content); $content = preg_replace(array_keys($fix), array_values($fix), $content);
//die("<pre>".htmlspecialchars($content)."</pre>\n"); //die("<pre>".htmlspecialchars($content)."</pre>\n");
} }
list($contentstart,$contentrepeat,$contentend) = preg_split('/\$\$pagerepeat\$\$/',$content,-1, PREG_SPLIT_NO_EMPTY)+[null,null,null]; //get differt parts of document, seperatet by Pagerepeat list($contentstart, $contentrepeat, $contentend) = preg_split('/\$\$pagerepeat\$\$/', $content, -1, PREG_SPLIT_NO_EMPTY) + [null,
null,
null]; //get differt parts of document, seperatet by Pagerepeat
if($mimetype == 'text/plain' && $ids && count($ids) > 1) if($mimetype == 'text/plain' && $ids && count($ids) > 1)
{ {
// textdocuments are simple, they do not hold start and end, but they may have content before and after the $$pagerepeat$$ tag // textdocuments are simple, they do not hold start and end, but they may have content before and after the $$pagerepeat$$ tag
// header and footer should not hold any $$ tags; if we find $$ tags with the header, we assume it is the pagerepeatcontent // header and footer should not hold any $$ tags; if we find $$ tags with the header, we assume it is the pagerepeatcontent
$nohead = false; $nohead = false;
if (stripos($contentstart,'$$') !== false) $nohead = true; if(stripos($contentstart, '$$') !== false)
{
$nohead = true;
}
if($nohead) if($nohead)
{ {
$contentend = $contentrepeat; $contentend = $contentrepeat;
@ -894,7 +965,8 @@ abstract class Merge
} }
} }
if (in_array($mimetype, array('application/vnd.oasis.opendocument.text','application/vnd.oasis.opendocument.text-template')) && count($ids) > 1) if(in_array($mimetype, array('application/vnd.oasis.opendocument.text',
'application/vnd.oasis.opendocument.text-template')) && count($ids) > 1)
{ {
if(strpos($content, '$$pagerepeat') === false) if(strpos($content, '$$pagerepeat') === false)
{ {
@ -914,7 +986,8 @@ abstract class Merge
list($contentstart, $contentrepeat, $contentend) = preg_split('/\$\$pagerepeat\$\$/', $content, -1, PREG_SPLIT_NO_EMPTY); //get different parts of document, seperated by pagerepeat list($contentstart, $contentrepeat, $contentend) = preg_split('/\$\$pagerepeat\$\$/', $content, -1, PREG_SPLIT_NO_EMPTY); //get different parts of document, seperated by pagerepeat
} }
} }
if (in_array($mimetype, array('application/vnd.ms-word.document.macroenabled.12', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')) && count($ids) > 1) if(in_array($mimetype, array('application/vnd.ms-word.document.macroenabled.12',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document')) && count($ids) > 1)
{ {
//for Word 2007 XML files we have to split the content and add a style for page break to the style area //for Word 2007 XML files we have to split the content and add a style for page break to the style area
list($contentstart, $contentrepeat, $contentend) = preg_split('/w:body>/', $content, -1, PREG_SPLIT_NO_EMPTY); //get differt parts of document, seperatet by Pagerepeat list($contentstart, $contentrepeat, $contentend) = preg_split('/w:body>/', $content, -1, PREG_SPLIT_NO_EMPTY); //get differt parts of document, seperatet by Pagerepeat
@ -923,7 +996,9 @@ abstract class Merge
$contentstart .= '<w:body>'; $contentstart .= '<w:body>';
$contentend = '</w:body></w:document>'; $contentend = '</w:body></w:document>';
} }
list($Labelstart,$Labelrepeat,$Labeltend) = preg_split('/\$\$label\$\$/',$contentrepeat,-1, PREG_SPLIT_NO_EMPTY)+[null,null,null]; //get the label content list($Labelstart, $Labelrepeat, $Labeltend) = preg_split('/\$\$label\$\$/', $contentrepeat, -1, PREG_SPLIT_NO_EMPTY) + [null,
null,
null]; //get the label content
preg_match_all('/\$\$labelplacement\$\$/', $contentrepeat, $countlables, PREG_SPLIT_NO_EMPTY); preg_match_all('/\$\$labelplacement\$\$/', $contentrepeat, $countlables, PREG_SPLIT_NO_EMPTY);
$countlables = count($countlables[0]); $countlables = count($countlables[0]);
preg_replace('/\$\$labelplacement\$\$/', '', $Labelrepeat, 1); preg_replace('/\$\$labelplacement\$\$/', '', $Labelrepeat, 1);
@ -934,7 +1009,10 @@ abstract class Merge
$ret = false; $ret = false;
return $ret; return $ret;
} }
if ($this->report_memory_usage) error_log(__METHOD__."(count(ids)=".count($ids).") strlen(contentrepeat)=".strlen($contentrepeat).', strlen(labelrepeat)='.strlen($Labelrepeat)); if($this->report_memory_usage)
{
error_log(__METHOD__ . "(count(ids)=" . count($ids) . ") strlen(contentrepeat)=" . strlen($contentrepeat) . ', strlen(labelrepeat)=' . strlen($Labelrepeat));
}
if($contentrepeat) if($contentrepeat)
{ {
@ -975,8 +1053,14 @@ abstract class Merge
} }
foreach((array)$ids as $n => $id) foreach((array)$ids as $n => $id)
{ {
if ($contentrepeat) $content = $contentrepeat; //content to repeat if($contentrepeat)
if ($lableprint) $content = $Labelrepeat; {
$content = $contentrepeat;
} //content to repeat
if($lableprint)
{
$content = $Labelrepeat;
}
// generate replacements; if exception is thrown, catch it set error message and return false // generate replacements; if exception is thrown, catch it set error message and return false
try try
@ -995,7 +1079,10 @@ abstract class Merge
$ret = false; $ret = false;
return $ret; return $ret;
} }
if ($this->report_memory_usage) error_log(__METHOD__."() $n: $id ".Api\Vfs::hsize(memory_get_usage(true))); if($this->report_memory_usage)
{
error_log(__METHOD__ . "() $n: $id " . Api\Vfs::hsize(memory_get_usage(true)));
}
// some general replacements: current user, date and time // some general replacements: current user, date and time
if(strpos($content, '$$user/') !== false && ($user = $GLOBALS['egw']->accounts->id2name($GLOBALS['egw_info']['user']['account_id'], 'person_id'))) if(strpos($content, '$$user/') !== false && ($user = $GLOBALS['egw']->accounts->id2name($GLOBALS['egw_info']['user']['account_id'], 'person_id')))
{ {
@ -1102,7 +1189,10 @@ abstract class Merge
rewind($content_stream); rewind($content_stream);
$content = stream_get_contents($content_stream); $content = stream_get_contents($content_stream);
} }
if ($this->report_memory_usage) error_log(__METHOD__."() returning ".Api\Vfs::hsize(memory_get_peak_usage(true))); if($this->report_memory_usage)
{
error_log(__METHOD__ . "() returning " . Api\Vfs::hsize(memory_get_peak_usage(true)));
}
return $content; return $content;
} }
@ -1156,7 +1246,10 @@ abstract class Merge
break; break;
default: // div. text files --> use our export-charset, defined in addressbook prefs default: // div. text files --> use our export-charset, defined in addressbook prefs
if (empty($charset)) $charset = $this->contacts->prefs['csv_charset']; if(empty($charset))
{
$charset = $this->contacts->prefs['csv_charset'];
}
break; break;
} }
//error_log(__METHOD__."('$document', ... ,$mimetype) --> $charset (egw=".Api\Translation::charset().', export='.$this->contacts->prefs['csv_charset'].')'); //error_log(__METHOD__."('$document', ... ,$mimetype) --> $charset (egw=".Api\Translation::charset().', export='.$this->contacts->prefs['csv_charset'].')');
@ -1249,10 +1342,13 @@ abstract class Merge
// Encode special chars so they don't break the file // Encode special chars so they don't break the file
$value = htmlspecialchars($value, ENT_NOQUOTES); $value = htmlspecialchars($value, ENT_NOQUOTES);
} }
else if (is_string($value) && (strpos($value,'<') !== false)) else
{
if(is_string($value) && (strpos($value, '<') !== false))
{ {
// Clean HTML, if it's being kept // Clean HTML, if it's being kept
if($replace_tags && extension_loaded('tidy')) { if($replace_tags && extension_loaded('tidy'))
{
$tidy = new tidy(); $tidy = new tidy();
$cleaned = $tidy->repairString($value, self::$tidy_config, 'utf8'); $cleaned = $tidy->repairString($value, self::$tidy_config, 'utf8');
// Found errors. Strip it all so there's some output // Found errors. Strip it all so there's some output
@ -1268,8 +1364,10 @@ abstract class Merge
} }
// replace </p> and <br /> with CRLF (remove <p> and CRLF) // replace </p> and <br /> with CRLF (remove <p> and CRLF)
$value = strip_tags(str_replace(array("\r", "\n", '<p>', '</p>', '<div>', '</div>', '<br />'), $value = strip_tags(str_replace(array("\r", "\n", '<p>', '</p>', '<div>', '</div>', '<br />'),
array('','','',"\r\n",'',"\r\n","\r\n"), $value), array('', '', '', "\r\n", '', "\r\n", "\r\n"), $value
implode('', $replace_tags)); ),
implode('', $replace_tags)
);
// Change <tag>...\r\n</tag> to <tag>...</tag>\r\n or simplistic line break below will mangle it // Change <tag>...\r\n</tag> to <tag>...</tag>\r\n or simplistic line break below will mangle it
// Loop to catch things like <b><span>Break:\r\n</span></b> // Loop to catch things like <b><span>Break:\r\n</span></b>
@ -1280,7 +1378,9 @@ abstract class Merge
{ {
$value = preg_replace('/<(b|strong|i|em|u|span)\b([^>]*?)>(.*?)' . "\r\n" . '<\/\1>/u', '<$1$2>$3</$1>' . "\r\n", $value, -1, $count); $value = preg_replace('/<(b|strong|i|em|u|span)\b([^>]*?)>(.*?)' . "\r\n" . '<\/\1>/u', '<$1$2>$3</$1>' . "\r\n", $value, -1, $count);
$i++; $i++;
} while($count > 0 && $i < 10); // Limit of 10 chosen arbitrarily just in case }
while($count > 0 && $i < 10); // Limit of 10 chosen arbitrarily just in case
}
} }
} }
// replace all control chars (C0+C1) but CR (\015), LF (\012) and TAB (\011) (eg. vertical tabulators) with space // replace all control chars (C0+C1) but CR (\015), LF (\012) and TAB (\011) (eg. vertical tabulators) with space
@ -1306,7 +1406,8 @@ abstract class Merge
if($this->date_fields || count($names)) if($this->date_fields || count($names))
{ {
$names = array(); $names = array();
foreach((array)$this->date_fields as $fieldname) { foreach((array)$this->date_fields as $fieldname)
{
$names[] = $fieldname; $names[] = $fieldname;
} }
$this->format_spreadsheet_dates($content, $names, $replacements, $mimetype . $mso_application_progid); $this->format_spreadsheet_dates($content, $names, $replacements, $mimetype . $mso_application_progid);
@ -1348,7 +1449,9 @@ abstract class Merge
// Check for encoded >< getting double-encoded // Check for encoded >< getting double-encoded
if($this->parse_html_styles) if($this->parse_html_styles)
{ {
$replacements = str_replace(array('&',"\r","\n",'&amp;lt;','&amp;gt;'),array('&amp;','',$break,'&lt;','&gt;'),$replacements); $replacements = str_replace(array('&', "\r", "\n", '&amp;lt;', '&amp;gt;'), array('&amp;', '', $break,
'&lt;',
'&gt;'), $replacements);
} }
else else
{ {
@ -1367,7 +1470,8 @@ abstract class Merge
$replacement =& $replacements['$$' . $parts[1] . '$$']; $replacement =& $replacements['$$' . $parts[1] . '$$'];
$replacement = preg_replace('/' . $parts[2] . '/', strtr($parts[3], array( $replacement = preg_replace('/' . $parts[2] . '/', strtr($parts[3], array(
'\\n' => "\n", '\\r' => "\r", '\\t' => "\t", '\\v' => "\v", '\\\\' => '\\', '\\f' => "\f", '\\n' => "\n", '\\r' => "\r", '\\t' => "\t", '\\v' => "\v", '\\\\' => '\\', '\\f' => "\f",
)), $replacement); )), $replacement
);
} }
else else
{ {
@ -1417,13 +1521,17 @@ abstract class Merge
if(!empty($format) && $names) if(!empty($format) && $names)
{ {
// Dealing with backtrack limit per AmigoJack 10-Jul-2010 comment on php.net preg-replace docs // Dealing with backtrack limit per AmigoJack 10-Jul-2010 comment on php.net preg-replace docs
do { do
{
$result = preg_replace($format, $replacement, $content, -1); $result = preg_replace($format, $replacement, $content, -1);
} }
// try to increase/double pcre.backtrack_limit failure // try to increase/double pcre.backtrack_limit failure
while(preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR && self::increase_backtrack_limit()); while(preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR && self::increase_backtrack_limit());
if ($result) $content = $result; // On failure $result would be NULL if($result)
{
$content = $result;
} // On failure $result would be NULL
} }
} }
@ -1444,9 +1552,12 @@ abstract class Merge
$memory_limit = ini_get('memory_limit'); $memory_limit = ini_get('memory_limit');
switch(strtoupper(substr($memory_limit, -1))) switch(strtoupper(substr($memory_limit, -1)))
{ {
case 'G': $memory_limit *= 1024; case 'G':
case 'M': $memory_limit *= 1024; $memory_limit *= 1024;
case 'K': $memory_limit *= 1024; case 'M':
$memory_limit *= 1024;
case 'K':
$memory_limit *= 1024;
} }
} }
if($backtrack_limit < $memory_limit / 8) if($backtrack_limit < $memory_limit / 8)
@ -1467,7 +1578,10 @@ abstract class Merge
'application/vnd.oasis.opendocument.spreadsheet', // open office calc 'application/vnd.oasis.opendocument.spreadsheet', // open office calc
'application/xmlExcel.Sheet', // Excel 2003 'application/xmlExcel.Sheet', // Excel 2003
//'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'//Excel WTF //'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'//Excel WTF
))) return; )))
{
return;
}
// Some different formats dates could be in, depending what they've been through // Some different formats dates could be in, depending what they've been through
$formats = array( $formats = array(
@ -1527,13 +1641,17 @@ abstract class Merge
if($format && $names) if($format && $names)
{ {
// Dealing with backtrack limit per AmigoJack 10-Jul-2010 comment on php.net preg-replace docs // Dealing with backtrack limit per AmigoJack 10-Jul-2010 comment on php.net preg-replace docs
do { do
{
$result = preg_replace($format, $replacement, $content, -1); $result = preg_replace($format, $replacement, $content, -1);
} }
// try to increase/double pcre.backtrack_limit failure // try to increase/double pcre.backtrack_limit failure
while(preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR && self::increase_backtrack_limit()); while(preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR && self::increase_backtrack_limit());
if ($result) $content = $result; // On failure $result would be NULL if($result)
{
$content = $result;
} // On failure $result would be NULL
} }
} }
@ -1563,7 +1681,7 @@ abstract class Merge
$expand_sub_cfs = []; $expand_sub_cfs = [];
foreach($sub as $index => $cf_sub) foreach($sub as $index => $cf_sub)
{ {
if(strpos($cf_sub, '#') === 0) if(str_starts_with($cf_sub, '#'))
{ {
$expand_sub_cfs[$cf[$index]] .= '$$' . $cf_sub . '$$ '; $expand_sub_cfs[$cf[$index]] .= '$$' . $cf_sub . '$$ ';
} }
@ -1577,14 +1695,18 @@ abstract class Merge
{ {
$field_app = $cfs[$field]['type']; $field_app = $cfs[$field]['type'];
} }
else if ($cfs[$field]['type'] == 'api-accounts' || $cfs[$field]['type'] == 'select-account') else
{
if($cfs[$field]['type'] == 'api-accounts' || $cfs[$field]['type'] == 'select-account')
{ {
// Special case for api-accounts -> contact // Special case for api-accounts -> contact
$field_app = 'addressbook'; $field_app = 'addressbook';
$account = $GLOBALS['egw']->accounts->read($values['#' . $field]); $account = $GLOBALS['egw']->accounts->read($values['#' . $field]);
$app_replacements[$field] = $this->contact_replacements($account['person_id']); $app_replacements[$field] = $this->contact_replacements($account['person_id']);
} }
else if (($list = explode('-',$cfs[$field]['type'])) && in_array($list[0], array_keys($GLOBALS['egw_info']['apps']))) else
{
if(($list = explode('-', $cfs[$field]['type'])) && in_array($list[0], array_keys($GLOBALS['egw_info']['apps'])))
{ {
// Sub-type - use app // Sub-type - use app
$field_app = $list[0]; $field_app = $list[0];
@ -1593,6 +1715,8 @@ abstract class Merge
{ {
continue; continue;
} }
}
}
// Get replacements for that application // Get replacements for that application
if(!$app_replacements[$field]) if(!$app_replacements[$field])
@ -1606,7 +1730,10 @@ abstract class Merge
} }
else else
{ {
if ($cfs[$field]['type'] == 'date' || $cfs[$field]['type'] == 'date-time') $this->date_fields[] = '#'.$field; if($cfs[$field]['type'] == 'date' || $cfs[$field]['type'] == 'date-time')
{
$this->date_fields[] = '#' . $field;
}
} }
} }
} }
@ -1729,19 +1856,22 @@ abstract class Merge
if(strpos($content, '$$IF') !== false) if(strpos($content, '$$IF') !== false)
{ //Example use to use: $$IF n_prefix~Herr~Sehr geehrter~Sehr geehrte$$ { //Example use to use: $$IF n_prefix~Herr~Sehr geehrter~Sehr geehrte$$
$this->replacements =& $replacements; $this->replacements =& $replacements;
$content = preg_replace_callback('/\$\$IF ([#0-9a-z_\/-]+)~(.*)~(.*)~(.*)\$\$/imU',Array($this,'replace_callback'),$content); $content = preg_replace_callback('/\$\$IF ([#0-9a-z_\/-]+)~(.*)~(.*)~(.*)\$\$/imU', array($this,
'replace_callback'), $content);
unset($this->replacements); unset($this->replacements);
} }
if(strpos($content, '$$NELF') !== false) if(strpos($content, '$$NELF') !== false)
{ //Example: $$NEPBR org_unit$$ sets a LF and value of org_unit, only if there is a value { //Example: $$NEPBR org_unit$$ sets a LF and value of org_unit, only if there is a value
$this->replacements =& $replacements; $this->replacements =& $replacements;
$content = preg_replace_callback('/\$\$NELF ([#0-9a-z_\/-]+)\$\$/imU',Array($this,'replace_callback'),$content); $content = preg_replace_callback('/\$\$NELF ([#0-9a-z_\/-]+)\$\$/imU', array($this,
'replace_callback'), $content);
unset($this->replacements); unset($this->replacements);
} }
if(strpos($content, '$$NENVLF') !== false) if(strpos($content, '$$NENVLF') !== false)
{ //Example: $$NEPBRNV org_unit$$ sets only a LF if there is a value for org_units, but did not add any value { //Example: $$NEPBRNV org_unit$$ sets only a LF if there is a value for org_units, but did not add any value
$this->replacements =& $replacements; $this->replacements =& $replacements;
$content = preg_replace_callback('/\$\$NENVLF ([#0-9a-z_\/-]+)\$\$/imU',Array($this,'replace_callback'),$content); $content = preg_replace_callback('/\$\$NENVLF ([#0-9a-z_\/-]+)\$\$/imU', array($this,
'replace_callback'), $content);
unset($this->replacements); unset($this->replacements);
} }
if(strpos($content, '$$LETTERPREFIX$$') !== false) if(strpos($content, '$$LETTERPREFIX$$') !== false)
@ -1752,7 +1882,8 @@ abstract class Merge
if(strpos($content, '$$LETTERPREFIXCUSTOM') !== false) if(strpos($content, '$$LETTERPREFIXCUSTOM') !== false)
{ //Example use to use for a custom Letter Prefix: $$LETTERPREFIX n_prefix title n_family$$ { //Example use to use for a custom Letter Prefix: $$LETTERPREFIX n_prefix title n_family$$
$this->replacements =& $replacements; $this->replacements =& $replacements;
$content = preg_replace_callback('/\$\$LETTERPREFIXCUSTOM ([#0-9a-z_-]+)(.*)\$\$/imU',Array($this,'replace_callback'),$content); $content = preg_replace_callback('/\$\$LETTERPREFIXCUSTOM ([#0-9a-z_-]+)(.*)\$\$/imU', array($this,
'replace_callback'), $content);
unset($this->replacements); unset($this->replacements);
} }
return $content; return $content;
@ -1766,8 +1897,14 @@ abstract class Merge
*/ */
private function replace_callback($param) private function replace_callback($param)
{ {
if (array_key_exists('$$'.$param[4].'$$',$this->replacements)) $param[4] = $this->replacements['$$'.$param[4].'$$']; if(array_key_exists('$$' . $param[4] . '$$', $this->replacements))
if (array_key_exists('$$'.$param[3].'$$',$this->replacements)) $param[3] = $this->replacements['$$'.$param[3].'$$']; {
$param[4] = $this->replacements['$$' . $param[4] . '$$'];
}
if(array_key_exists('$$' . $param[3] . '$$', $this->replacements))
{
$param[3] = $this->replacements['$$' . $param[3] . '$$'];
}
$pattern = '/' . preg_quote($param[2], '/') . '/'; $pattern = '/' . preg_quote($param[2], '/') . '/';
if(strpos($param[0], '$$IF') === 0 && (trim($param[2]) == "EMPTY" || $param[2] === '')) if(strpos($param[0], '$$IF') === 0 && (trim($param[2]) == "EMPTY" || $param[2] === ''))
@ -1828,16 +1965,25 @@ abstract class Merge
default: default:
$LF = "\n"; $LF = "\n";
} }
if($is_xml) { if($is_xml)
$this->replacements = str_replace(array('&','&amp;amp;','<','>',"\r","\n"),array('&amp;','&amp;','&lt;','&gt;','',$LF),$this->replacements); {
$this->replacements = str_replace(array('&', '&amp;amp;', '<', '>', "\r", "\n"), array('&amp;', '&amp;',
'&lt;', '&gt;', '',
$LF), $this->replacements);
} }
if(strpos($param[0], '$$NELF') === 0) if(strpos($param[0], '$$NELF') === 0)
{ //sets a Pagebreak and value, only if the field has a value { //sets a Pagebreak and value, only if the field has a value
if ($this->replacements['$$'.$param[1].'$$'] !='') $replace = $LF.$this->replacements['$$'.$param[1].'$$']; if($this->replacements['$$' . $param[1] . '$$'] != '')
{
$replace = $LF . $this->replacements['$$' . $param[1] . '$$'];
}
} }
if(strpos($param[0], '$$NENVLF') === 0) if(strpos($param[0], '$$NENVLF') === 0)
{ //sets a Pagebreak without any value, only if the field has a value { //sets a Pagebreak without any value, only if the field has a value
if ($this->replacements['$$'.$param[1].'$$'] !='') $replace = $LF; if($this->replacements['$$' . $param[1] . '$$'] != '')
{
$replace = $LF;
}
} }
if(strpos($param[0], '$$LETTERPREFIXCUSTOM') === 0) if(strpos($param[0], '$$LETTERPREFIXCUSTOM') === 0)
{ //sets a Letterprefix { //sets a Letterprefix
@ -1845,7 +1991,10 @@ abstract class Merge
$replaceprefix = explode(' ', substr($param[0], 21, -2)); $replaceprefix = explode(' ', substr($param[0], 21, -2));
foreach($replaceprefix as $nameprefix) foreach($replaceprefix as $nameprefix)
{ {
if ($this->replacements['$$'.$nameprefix.'$$'] !='') $replaceprefixsort[] = $this->replacements['$$'.$nameprefix.'$$']; if($this->replacements['$$' . $nameprefix . '$$'] != '')
{
$replaceprefixsort[] = $this->replacements['$$' . $nameprefix . '$$'];
}
} }
$replace = implode(' ', $replaceprefixsort); $replace = implode(' ', $replaceprefixsort);
} }
@ -1913,13 +2062,19 @@ abstract class Merge
} }
//error_log(__METHOD__.__LINE__.' Message after importMessageToMergeAndSend:'.array2string($msgs)); //error_log(__METHOD__.__LINE__.' Message after importMessageToMergeAndSend:'.array2string($msgs));
$retString = ''; $retString = '';
if (count($msgs['success'])>0) $retString .= count($msgs['success']).' '.(count($msgs['success'])+count($msgs['failed'])==1?lang('Message prepared for sending.'):lang('Message(s) send ok.'));//implode('<br />',$msgs['success']); if(count($msgs['success']) > 0)
{
$retString .= count($msgs['success']) . ' ' . (count($msgs['success']) + count($msgs['failed']) == 1 ? lang('Message prepared for sending.') : lang('Message(s) send ok.'));
}//implode('<br />',$msgs['success']);
//if (strlen($retString)>0) $retString .= '<br />'; //if (strlen($retString)>0) $retString .= '<br />';
foreach($msgs['failed'] as $c => $e) foreach($msgs['failed'] as $c => $e)
{ {
$errorString .= lang('contact') . ' ' . lang('id') . ':' . $c . '->' . $e . '.'; $errorString .= lang('contact') . ' ' . lang('id') . ':' . $c . '->' . $e . '.';
} }
if (count($msgs['failed'])>0) $retString .= count($msgs['failed']).' '.lang('Message(s) send failed!').'=>'.$errorString; if(count($msgs['failed']) > 0)
{
$retString .= count($msgs['failed']) . ' ' . lang('Message(s) send failed!') . '=>' . $errorString;
}
return $retString; return $retString;
case 'application/vnd.oasis.opendocument.text': case 'application/vnd.oasis.opendocument.text':
case 'application/vnd.oasis.opendocument.spreadsheet': case 'application/vnd.oasis.opendocument.spreadsheet':
@ -1929,12 +2084,24 @@ abstract class Merge
case 'application/vnd.oasis.opendocument.presentation-template': case 'application/vnd.oasis.opendocument.presentation-template':
switch($mimetype) switch($mimetype)
{ {
case 'application/vnd.oasis.opendocument.text': $ext = '.odt'; break; case 'application/vnd.oasis.opendocument.text':
case 'application/vnd.oasis.opendocument.spreadsheet': $ext = '.ods'; break; $ext = '.odt';
case 'application/vnd.oasis.opendocument.presentation': $ext = '.odp'; break; break;
case 'application/vnd.oasis.opendocument.text-template': $ext = '.ott'; break; case 'application/vnd.oasis.opendocument.spreadsheet':
case 'application/vnd.oasis.opendocument.spreadsheet-template': $ext = '.ots'; break; $ext = '.ods';
case 'application/vnd.oasis.opendocument.presentation-template': $ext = '.otp'; break; break;
case 'application/vnd.oasis.opendocument.presentation':
$ext = '.odp';
break;
case 'application/vnd.oasis.opendocument.text-template':
$ext = '.ott';
break;
case 'application/vnd.oasis.opendocument.spreadsheet-template':
$ext = '.ots';
break;
case 'application/vnd.oasis.opendocument.presentation-template':
$ext = '.otp';
break;
} }
$archive = tempnam($GLOBALS['egw_info']['server']['temp_dir'], basename($document, $ext) . '-') . $ext; $archive = tempnam($GLOBALS['egw_info']['server']['temp_dir'], basename($document, $ext) . '-') . $ext;
copy($content_url, $archive); copy($content_url, $archive);
@ -2005,7 +2172,10 @@ abstract class Merge
return $err; return $err;
} }
} }
if ($this->report_memory_usage) error_log(__METHOD__."() after HTML processing ".Api\Vfs::hsize(memory_get_peak_usage(true))); if($this->report_memory_usage)
{
error_log(__METHOD__ . "() after HTML processing " . Api\Vfs::hsize(memory_get_peak_usage(true)));
}
} }
if(!empty($name)) if(!empty($name))
{ {
@ -2026,13 +2196,25 @@ abstract class Merge
if($zip->open($archive, ZipArchive::CHECKCONS) !== true) if($zip->open($archive, ZipArchive::CHECKCONS) !== true)
{ {
error_log(__METHOD__ . __LINE__ . " !ZipArchive::open('$archive',ZIPARCHIVE" . "::CHECKCONS) failed. Trying open without validating"); error_log(__METHOD__ . __LINE__ . " !ZipArchive::open('$archive',ZIPARCHIVE" . "::CHECKCONS) failed. Trying open without validating");
if ($zip->open($archive) !== true) throw new Api\Exception("!ZipArchive::open('$archive',|ZIPARCHIVE::CHECKCONS)"); if($zip->open($archive) !== true)
{
throw new Api\Exception("!ZipArchive::open('$archive',|ZIPARCHIVE::CHECKCONS)");
}
}
if($zip->addFromString($content_file, $merged) !== true)
{
throw new Api\Exception("!ZipArchive::addFromString('$content_file',\$merged)");
}
if($zip->close() !== true)
{
throw new Api\Exception("!ZipArchive::close()");
} }
if ($zip->addFromString($content_file,$merged) !== true) throw new Api\Exception("!ZipArchive::addFromString('$content_file',\$merged)");
if ($zip->close() !== true) throw new Api\Exception("!ZipArchive::close()");
unset($zip); unset($zip);
unset($merged); unset($merged);
if ($this->report_memory_usage) error_log(__METHOD__."() after ZIP processing ".Api\Vfs::hsize(memory_get_peak_usage(true))); if($this->report_memory_usage)
{
error_log(__METHOD__ . "() after ZIP processing " . Api\Vfs::hsize(memory_get_peak_usage(true)));
}
$header['filesize'] = filesize($archive); $header['filesize'] = filesize($archive);
} }
else else
@ -2068,9 +2250,18 @@ abstract class Merge
*/ */
public function download_by_request() public function download_by_request()
{ {
if(empty($_POST['data_document_name'])) return false; if(empty($_POST['data_document_name']))
if(empty($_POST['data_document_dir'])) return false; {
if(empty($_POST['data_checked'])) return false; return false;
}
if(empty($_POST['data_document_dir']))
{
return false;
}
if(empty($_POST['data_checked']))
{
return false;
}
return $this->download( return $this->download(
$_POST['data_document_name'], $_POST['data_document_name'],
@ -2091,7 +2282,10 @@ abstract class Merge
public static function get_documents($dirs, $prefix = 'document_', $mime_filter = null, $app = '') public static function get_documents($dirs, $prefix = 'document_', $mime_filter = null, $app = '')
{ {
$export_limit = self::getExportLimit($app); $export_limit = self::getExportLimit($app);
if (!$dirs || (!self::hasExportLimit($export_limit,'ISALLOWED') && !self::is_export_limit_excepted())) return array(); if(!$dirs || (!self::hasExportLimit($export_limit, 'ISALLOWED') && !self::is_export_limit_excepted()))
{
return array();
}
// split multiple comma or whitespace separated directories // split multiple comma or whitespace separated directories
// to still allow space or comma in dirnames, we also use the trailing slash of all pathes to split // to still allow space or comma in dirnames, we also use the trailing slash of all pathes to split
@ -2099,7 +2293,10 @@ abstract class Merge
{ {
foreach($dirs as $n => &$d) foreach($dirs as $n => &$d)
{ {
if ($n) $d = '/'.$d; // re-adding trailing slash removed by split if($n)
{
$d = '/' . $d;
} // re-adding trailing slash removed by split
} }
} }
if($mime_filter && ($negativ_filter = $mime_filter[0] === '!')) if($mime_filter && ($negativ_filter = $mime_filter[0] === '!'))
@ -2122,8 +2319,14 @@ abstract class Merge
{ {
// return only the mime-types we support // return only the mime-types we support
$parts = explode('.', $file['name']); $parts = explode('.', $file['name']);
if (!self::is_implemented($file['mime'],'.'.array_pop($parts))) continue; if(!self::is_implemented($file['mime'], '.' . array_pop($parts)))
if ($mime_filter && $negativ_filter === in_array($file['mime'], (array)$mime_filter)) continue; {
continue;
}
if($mime_filter && $negativ_filter === in_array($file['mime'], (array)$mime_filter))
{
continue;
}
$list[$prefix . $file['name']] = Api\Vfs::decodePath($file['name']); $list[$prefix . $file['name']] = Api\Vfs::decodePath($file['name']);
} }
} }
@ -2153,9 +2356,13 @@ abstract class Merge
$export_limit = null) $export_limit = null)
{ {
$documents = array(); $documents = array();
if ($export_limit == null) $export_limit = self::getExportLimit(); // check if there is a globalsetting if($export_limit == null)
{
$export_limit = self::getExportLimit();
} // check if there is a globalsetting
try { try
{
if(class_exists('EGroupware\\collabora\\Bo') && if(class_exists('EGroupware\\collabora\\Bo') &&
$GLOBALS['egw_info']['user']['apps']['collabora'] && $GLOBALS['egw_info']['user']['apps']['collabora'] &&
($discovery = \EGroupware\collabora\Bo::discover()) && ($discovery = \EGroupware\collabora\Bo::discover()) &&
@ -2198,7 +2405,10 @@ abstract class Merge
{ {
foreach($dirs as $n => &$d) foreach($dirs as $n => &$d)
{ {
if ($n) $d = '/'.$d; // re-adding trailing slash removed by split if($n)
{
$d = '/' . $d;
} // re-adding trailing slash removed by split
} }
} }
foreach($dirs as $dir) foreach($dirs as $dir)
@ -2280,7 +2490,9 @@ abstract class Merge
} }
} }
} }
else if (count($files) >= self::SHOW_DOCS_BY_MIME_LIMIT) else
{
if(count($files) >= self::SHOW_DOCS_BY_MIME_LIMIT)
{ {
if(!isset($documents[$file['mime']])) if(!isset($documents[$file['mime']]))
{ {
@ -2308,6 +2520,7 @@ abstract class Merge
} }
} }
} }
}
// Add PDF checkbox // Add PDF checkbox
$documents['as_pdf'] = array( $documents['as_pdf'] = array(
@ -2337,7 +2550,7 @@ abstract class Merge
* @param Array $file Array of information about the document from Api\Vfs::find * @param Array $file Array of information about the document from Api\Vfs::find
* @return void * @return void
*/ */
private static function document_mail_action(Array &$action, $file) private static function document_mail_action(array &$action, $file)
{ {
unset($action['postSubmit']); unset($action['postSubmit']);
unset($action['onExecute']); unset($action['onExecute']);
@ -2371,7 +2584,7 @@ abstract class Merge
* @param Array $file Array of information about the document from Api\Vfs::find * @param Array $file Array of information about the document from Api\Vfs::find
* @return void * @return void
*/ */
private static function document_editable_action(Array &$action, $file) private static function document_editable_action(array &$action, $file)
{ {
static $action_base = array( static $action_base = array(
// The same for every file // The same for every file
@ -2416,7 +2629,10 @@ abstract class Merge
{ {
foreach($dirs as $n => $dir) foreach($dirs as $n => $dir)
{ {
if ($n) $dir = '/'.$dir; // re-adding trailing slash removed by split if($n)
{
$dir = '/' . $dir;
} // re-adding trailing slash removed by split
if(Api\Vfs::stat($dir . '/' . $document) && Api\Vfs::is_readable($dir . '/' . $document)) if(Api\Vfs::stat($dir . '/' . $document) && Api\Vfs::is_readable($dir . '/' . $document))
{ {
$document = $dir . '/' . $document; $document = $dir . '/' . $document;
@ -2572,7 +2788,7 @@ abstract class Merge
$name = str_replace( $name = str_replace(
array_keys($placeholders), array_keys($placeholders),
array_values($placeholders), array_values($placeholders),
is_array($pref) ? implode(' ', $pref) : $pref is_array($pref) ? implode(' ', $pref) : str_replace(',', ', ', $pref)
); );
} }
return $name; return $name;
@ -2612,7 +2828,7 @@ abstract class Merge
); );
// Check for a configured preferred directory // Check for a configured preferred directory
if(($pref = $GLOBALS['egw_info']['user']['preferences']['filemanager'][Merge::PREF_STORE_LOCATION]) && Vfs::is_writable($pref)) if(($pref = $GLOBALS['egw_info']['user']['preferences'][$this->get_app()][Merge::PREF_STORE_LOCATION]) && Vfs::is_writable($pref))
{ {
$target = $pref; $target = $pref;
} }
@ -2633,7 +2849,7 @@ abstract class Merge
// Get app // Get app
list($appname, $_merge) = explode('_', get_class($merge)); list($appname, $_merge) = explode('_', get_class($merge));
if($merge instanceOf Api\Contacts\Merge) if($merge instanceof Api\Contacts\Merge)
{ {
$appname = 'addressbook'; $appname = 'addressbook';
} }
@ -2678,7 +2894,10 @@ abstract class Merge
$ui->$get_rows($session, $rows, $readonlys); $ui->$get_rows($session, $rows, $readonlys);
foreach($rows as $row_number => $row) foreach($rows as $row_number => $row)
{ {
if(!is_numeric($row_number)) continue; if(!is_numeric($row_number))
{
continue;
}
$row_id = $row[$session['row_id'] ? $session['row_id'] : 'id']; $row_id = $row[$session['row_id'] ? $session['row_id'] : 'id'];
switch(get_class($merge)) switch(get_class($merge))
{ {
@ -2688,7 +2907,10 @@ abstract class Merge
break; break;
case \timesheet_merge::class: case \timesheet_merge::class:
// Skip the rows with totalss // Skip the rows with totalss
if(!is_numeric($row_id)) continue 2; // +1 for switch if(!is_numeric($row_id))
{
continue 2;
} // +1 for switch
// Fall through // Fall through
default: default:
$ids[] = $row_id; $ids[] = $row_id;
@ -2719,7 +2941,10 @@ abstract class Merge
*/ */
static public function number_format($number, $num_decimal_places = 2, $_mimetype = '') static public function number_format($number, $num_decimal_places = 2, $_mimetype = '')
{ {
if ((string)$number === '') return ''; if((string)$number === '')
{
return '';
}
//error_log(__METHOD__.$_mimetype); //error_log(__METHOD__.$_mimetype);
switch($_mimetype) switch($_mimetype)
{ {
@ -2914,4 +3139,63 @@ abstract class Merge
*/ */
$placeholder_list[$base_name] = $add_placeholder_groups; $placeholder_list[$base_name] = $add_placeholder_groups;
} }
/**
* Get preference settings
*
* Merge has some preferences that the same across apps, but can have different values for each app:
* - Default document
* - Document template directory
* - Filename customization
* - Generated document target directory
*/
public function merge_preferences()
{
$settings = array();
$settings[self::PREF_DEFAULT_TEMPLATE] = array(
'type' => 'vfs_file',
'size' => 60,
'label' => 'Default document to insert entries',
'name' => self::PREF_DEFAULT_TEMPLATE,
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.', lang($this->get_app())) . ' ' .
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'name') . ' ' .
lang('The following document-types are supported:') . implode(',', self::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
);
$settings[self::PREF_TEMPLATE_DIR] = array(
'type' => 'vfs_dirs',
'size' => 60,
'label' => 'Directory with documents to insert entries',
'name' => self::PREF_TEMPLATE_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($this->get_app())) . ' ' .
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'name') . ' ' .
lang('The following document-types are supported:') . implode(',', self::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/' . $this->get_app(),
);
$settings[self::PREF_STORE_LOCATION] = array(
'type' => 'vfs_dir',
'size' => 60,
'label' => 'Directory for storing merged documents',
'name' => self::PREF_STORE_LOCATION,
'help' => lang('When you merge entries into documents, they will be stored here. If no directory is provided, they will be stored in your home directory (%1)', Vfs::get_home_dir())
);
$settings[self::PREF_DOCUMENT_FILENAME] = array(
'type' => 'taglist',
'label' => 'Merged document filename',
'name' => self::PREF_DOCUMENT_FILENAME,
'values' => self::DOCUMENT_FILENAME_OPTIONS,
'help' => 'Choose the default filename for merged documents.',
'xmlrpc' => True,
'admin' => False,
'default' => 'document',
);
return $settings;
}
} }

View File

@ -660,31 +660,8 @@ class calendar_hooks
// Merge print // Merge print
if ($GLOBALS['egw_info']['user']['apps']['filemanager']) if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
{ {
$settings['default_document'] = array( $merge = new calendar_merge();
'type' => 'vfs_file', $settings += $merge->merge_preferences();
'size' => 60,
'label' => 'Default document to insert entries',
'name' => 'default_document',
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.',lang('calendar')).' '.
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','calendar_title').' '.
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'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('calendar')).' '.
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','calendar_title').' '.
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/calendar',
);
} }
$settings += array( $settings += array(

View File

@ -171,38 +171,8 @@ class filemanager_hooks
), ),
); );
$settings[Api\Storage\Merge::PREF_STORE_LOCATION] = array( $merge = new filemanager_merge();
'type' => 'vfs_dir', $settings += $merge->merge_preferences();
'size' => 60,
'label' => 'Directory for storing merged documents',
'name' => Api\Storage\Merge::PREF_STORE_LOCATION,
'help' => lang('When you merge entries into documents, they will be stored here. If no directory is provided, they will be stored in %1', Vfs::get_home_dir())
);
$settings['default_document'] = array(
'type' => 'vfs_file',
'size' => 60,
'label' => 'Default document to insert entries',
'name' => 'default_document',
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.', lang('filemanager')) . ' ' .
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'name') . ' ' .
lang('The following document-types are supported:') . implode(',', Api\Storage\Merge::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'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('filemanager')).' '.
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','name').' '.
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
'default' => '/templates/filemanager',
);
$editorLink = self::getEditorLink(); $editorLink = self::getEditorLink();
$mimes = array('0' => lang('None')); $mimes = array('0' => lang('None'));

View File

@ -458,41 +458,8 @@ class infolog_hooks
// Merge print // Merge print
if ($GLOBALS['egw_info']['user']['apps']['filemanager']) if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
{ {
$settings['default_document'] = array( $merge = new infolog_merge();
'type' => 'vfs_file', $settings += $merge->merge_preferences();
'size' => 60,
'label' => 'Default document to insert entries',
'name' => 'default_document',
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon 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,
);
$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',
'run_lang' => false,
'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',
);
} }
if ($GLOBALS['egw_info']['user']['apps']['calendar']) if ($GLOBALS['egw_info']['user']['apps']['calendar'])

View File

@ -178,41 +178,8 @@ class timesheet_hooks
// Merge print // Merge print
if ($GLOBALS['egw_info']['user']['apps']['filemanager']) if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
{ {
$settings['default_document'] = array( $merge = new timesheet_merge();
'type' => 'vfs_file', $settings += $merge->merge_preferences();
'size' => 60,
'label' => 'Default document to insert entries',
'name' => 'default_document',
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the 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,
);
$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()),
'run_lang' => false,
'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',
);
} }
return $settings; return $settings;