Add placeholder list for calendar & filemanager

This commit is contained in:
nathan 2021-10-07 13:45:49 -06:00
parent 417bbccbe3
commit c73a7beac8
4 changed files with 559 additions and 310 deletions

View File

@ -227,8 +227,9 @@ export class et2_placeholder_select extends et2_inputWidget
app.onchange = (node, widget) =>
{
preview.set_value("");
if(['user'].indexOf(widget.get_value()) >= 0)
if(['user', 'filemanager'].indexOf(widget.get_value()) >= 0)
{
// These ones don't let you select an entry for preview (they don't work)
entry.set_disabled(true);
entry.app_select.val('user');
entry.set_value({app: 'user', id: '', query: ''});

View File

@ -64,9 +64,13 @@ class Placeholder extends Etemplate\Widget
if(is_null($apps))
{
$apps = ['addressbook', 'user', 'general'] +
$apps = array_merge(
['addressbook', 'user', 'general'],
// We use linking for preview, so limit to apps that support links
array_keys(Api\Link::app_list('query'));
array_keys(Api\Link::app_list('query')),
// Filemanager doesn't support links, but add it anyway
['filemanager']
);
}
foreach($apps as $appname)
@ -86,6 +90,8 @@ class Placeholder extends Etemplate\Widget
// Looks like app doesn't support merging
continue 2;
}
Api\Translation::load_app($appname, $GLOBALS['egw_info']['user']['preferences']['common']['lang']);
$list = method_exists($merge, 'get_placeholder_list') ? $merge->get_placeholder_list() : [];
break;
}

View File

@ -91,10 +91,12 @@ class calendar_merge extends Api\Storage\Merge
{
$this->table_plugins[date('l', strtotime("+$i days"))] = 'day_plugin';
}
for($i = 1; $i <= 31; $i++) {
for($i = 1; $i <= 31; $i++)
{
$this->table_plugins['day_' . $i] = 'day'; // Numerically by day number (1-31)
}
foreach(self::$relative as $day) {
foreach(self::$relative as $day)
{
$this->table_plugins[$day] = 'day'; // Current day
}
$this->query = is_array($this->bo->cal_prefs['saved_states']) ?
@ -144,10 +146,12 @@ class calendar_merge extends Api\Storage\Merge
'offset' => 0,
'order' => 'cal_start',
'cfs' => strpos($content, '#') !== false ? array_keys(Api\Storage\Customfields::get('calendar')) : null
));
)
);
if(strpos($content, '$$calendar/') !== false || strpos($content, '$$table/day') !== false)
{
array_unshift($events,false); unset($events[0]); // renumber the array to start with key 1, instead of 0
array_unshift($events, false);
unset($events[0]); // renumber the array to start with key 1, instead of 0
$prefix = 'calendar/%d';
}
}
@ -198,7 +202,8 @@ class calendar_merge extends Api\Storage\Merge
public function calendar_replacements($id, $prefix = '', &$content = '')
{
$replacements = array();
if(!is_array($id) || !$id['start']) {
if(!is_array($id) || !$id['start'])
{
if(is_string($id) && strpos($id, ':'))
{
$_id = $id;
@ -206,7 +211,9 @@ class calendar_merge extends Api\Storage\Merge
list($id['id'], $id['recur_date']) = explode(':', $_id);
}
$event = $this->bo->read(is_array($id) ? $id['id'] : $id, is_array($id) ? $id['recur_date'] : null);
} else {
}
else
{
$event = $id;
}
@ -270,7 +277,10 @@ class calendar_merge extends Api\Storage\Merge
) as $name => $format)
{
$value = Api\DateTime::to($event[$what], $format);
if ($format == 'l') $value = lang($value);
if($format == 'l')
{
$value = lang($value);
}
$replacements['$$' . ($prefix ? $prefix . '/' : '') . 'calendar_' . $what . $name . '$$'] = $value;
}
}
@ -362,7 +372,8 @@ class calendar_merge extends Api\Storage\Merge
}
$participant_status = array('A' => 0, 'R' => 0, 'T' => 0, 'U' => 0, 'D' => 0);
$status_label = array('A' => 'accepted', 'R' => 'rejected', 'T' => 'tentative', 'U' => 'unknown', 'D' => 'delegated');
$status_label = array('A' => 'accepted', 'R' => 'rejected', 'T' => 'tentative', 'U' => 'unknown',
'D' => 'delegated');
$participant_summary = count($record->participants) . ' ' . lang('Participants') . ': ';
$status_totals = [];
@ -399,18 +410,32 @@ class calendar_merge extends Api\Storage\Merge
public function day_plugin($plugin, $date, $n, $repeat)
{
static $days = null;
if(is_array($date) && !$date['start']) {
if(is_array($date) && !$date['start'])
{
// List of IDs
if($date[0]['start']) {
if($date[0]['start'])
{
$id = array('start' => PHP_INT_MAX, 'end' => 0);
foreach($date as $event) {
if($event['start'] && $event['start'] < $id['start']) $id['start'] = $event['start'];
if($event['end'] && $event['end'] > $id['end']) $id['end'] = $event['end'];
foreach($date as $event)
{
if($event['start'] && $event['start'] < $id['start'])
{
$id['start'] = $event['start'];
}
if($event['end'] && $event['end'] > $id['end'])
{
$id['end'] = $event['end'];
}
}
$date = $id;
} else {
}
else
{
$event = $this->bo->read(is_array($date) ? $date['id'] : $date, is_array($date) ? $date['recur_date'] : null);
if(date('l',$event['start']) != $plugin) return array();
if(date('l', $event['start']) != $plugin)
{
return array();
}
$date = $event['start'];
}
}
@ -426,7 +451,8 @@ class calendar_merge extends Api\Storage\Merge
'order' => 'cal_start',
'daywise' => true,
'cfs' => array(), // read all custom-fields
));
)
);
if (true) $days = array();
$replacements = array();
@ -436,13 +462,19 @@ class calendar_merge extends Api\Storage\Merge
foreach($list as $event)
{
$event_id = $event['id'] . ($event['recur_date'] ? ':' . $event['recur_date'] : '');
if($this->ids && !in_array($event_id, $this->ids)) continue;
if($this->ids && !in_array($event_id, $this->ids))
{
continue;
}
$start = Api\DateTime::to($event['start'], 'array');
$end = Api\DateTime::to($event['end'], 'array');
$replacements = $this->calendar_replacements($event);
if($start['year'] == $end['year'] && $start['month'] == $end['month'] && $start['day'] == $end['day']) {
if($start['year'] == $end['year'] && $start['month'] == $end['month'] && $start['day'] == $end['day'])
{
$dow = date('l', $event['start']);
} else {
}
else
{
$dow = date('l', strtotime($day));
// Fancy date+time formatting for multi-day events
$replacements['$$calendar_starttime$$'] = date($time_format, $day == date('Ymd', $event['start']) ? $event['start'] : mktime(0, 0, 0, 0, 0, 1));
@ -451,12 +483,14 @@ class calendar_merge extends Api\Storage\Merge
$days[date('Ymd', $_date)][$dow][] = $replacements;
}
if(strpos($repeat, 'day/date') !== false || strpos($repeat, 'day/name') !== false) {
if(strpos($repeat, 'day/date') !== false || strpos($repeat, 'day/name') !== false)
{
$date_marker = array(
'$$day/date$$' => date($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'], strtotime($day)),
'$$day/name$$' => lang(date('l', strtotime($day)))
);
if(!is_array($days[date('Ymd',$_date)][date('l',strtotime($day))])) {
if(!is_array($days[date('Ymd', $_date)][date('l', strtotime($day))]))
{
$blank = $this->calendar_replacements(array());
foreach($blank as &$value)
{
@ -497,7 +531,10 @@ class calendar_merge extends Api\Storage\Merge
$arr = $this->bo->date2array($id['start']);
$arr['day'] = $which;
$date = $this->bo->date2ts($arr);
if(is_array($id) && $id['start'] && ($date < $id['start'] || $date > $id['end'])) return array();
if(is_array($id) && $id['start'] && ($date < $id['start'] || $date > $id['end']))
{
return array();
}
}
elseif($plugin == 'selected')
{
@ -507,10 +544,17 @@ class calendar_merge extends Api\Storage\Merge
{
$date = strtotime($plugin);
}
if($type == 'day' && is_array($id) && !$id['start']) {
if($type == 'day' && is_array($id) && !$id['start'])
{
$event = $this->bo->read(is_array($id) ? $id['id'] : $id, is_array($id) ? $id['recur_date'] : null);
if($which && date('d',$event['start']) != $which) return array();
if(date('Ymd',$date) != date('Ymd', $event['start'])) return array();
if($which && date('d', $event['start']) != $which)
{
return array();
}
if(date('Ymd', $date) != date('Ymd', $event['start']))
{
return array();
}
return $n == 0 ? $this->calendar_replacements($event) : array();
}
@ -526,7 +570,8 @@ class calendar_merge extends Api\Storage\Merge
'order' => 'cal_start',
'daywise' => true,
'cfs' => array(), // read all custom-fields
));
)
);
$replacements = array();
if (true) $days = array();
@ -536,25 +581,33 @@ class calendar_merge extends Api\Storage\Merge
foreach($list as $event)
{
$event_id = $event['id'] . ($event['recur_date'] ? ':' . $event['recur_date'] : '');
if($this->ids && !in_array($event_id, $this->ids)) continue;
if($this->ids && !in_array($event_id, $this->ids))
{
continue;
}
$start = Api\DateTime::to($event['start'], 'array');
$end = Api\DateTime::to($event['end'], 'array');
$replacements = $this->calendar_replacements($event);
if($start['year'] == $end['year'] && $start['month'] == $end['month'] && $start['day'] == $end['day']) {
if($start['year'] == $end['year'] && $start['month'] == $end['month'] && $start['day'] == $end['day'])
{
//$dow = date('l',$event['start']);
} else {
}
else
{
// Fancy date+time formatting for multi-day events
$replacements['$$calendar_starttime$$'] = date($time_format, $day == date('Ymd', $event['start']) ? $event['start'] : mktime(0, 0, 0, 0, 0, 1));
$replacements['$$calendar_endtime$$'] = date($time_format, $day == date('Ymd', $event['end']) ? $event['end'] : mktime(23, 59, 59, 0, 0, 0));
}
$days[date('Ymd', $_date)][$plugin][] = $replacements;
}
if(strpos($repeat, 'day/date') !== false || strpos($repeat, 'day/name') !== false) {
if(strpos($repeat, 'day/date') !== false || strpos($repeat, 'day/name') !== false)
{
$date_marker = array(
'$$day/date$$' => date($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'], strtotime($day)),
'$$day/name$$' => lang(date('l', strtotime($day)))
);
if(!is_array($days[date('Ymd',$_date)][$plugin])) {
if(!is_array($days[date('Ymd', $_date)][$plugin]))
{
$blank = $this->calendar_replacements(array());
foreach($blank as &$value)
{
@ -587,22 +640,35 @@ class calendar_merge extends Api\Storage\Merge
{
unset($plugin); // not used, but required by function signature
if(!is_array($id) || !$id['start']) {
if(!is_array($id) || !$id['start'])
{
$event = $this->bo->read(is_array($id) ? $id['id'] : $id, is_array($id) ? $id['recur_date'] : null);
} else {
}
else
{
$event = $id;
}
if(!is_array($event['participants']) || $n >= count($event['participants'])) return array();
if(!is_array($event['participants']) || $n >= count($event['participants']))
{
return array();
}
$participant = null;
$status = null;
$i = -1;
foreach($event['participants'] as $participant => $status) {
if(++$i == $n) break;
foreach($event['participants'] as $participant => $status)
{
if(++$i == $n)
{
break;
}
}
if(!$participant) return array();
if(!$participant)
{
return array();
}
// Add some common information
$quantity = $role = null;
@ -636,7 +702,10 @@ class calendar_merge extends Api\Storage\Merge
$replacements = $this->contact_replacements(substr($participant, 1), '');
break;
case 'r':
if (is_null(self::$resources)) self::$resources = new resources_bo();
if(is_null(self::$resources))
{
self::$resources = new resources_bo();
}
if(($resource = self::$resources->read(substr($participant, 1))))
{
foreach($resource as $name => $value)
@ -666,7 +735,7 @@ class calendar_merge extends Api\Storage\Merge
protected function get_birthdays($day)
{
$contacts = new Api\Contacts();
$birthdays = Array();
$birthdays = array();
foreach($contacts->get_addressbooks() as $owner => $name)
{
$birthdays += $contacts->read_birthdays($owner, substr($day, 0, 4));
@ -686,13 +755,13 @@ class calendar_merge extends Api\Storage\Merge
* @param String $content Template content, used to determine what style of
* ID is needed.
*/
protected function validate_ids(Array $ids, $content)
protected function validate_ids(array $ids, $content)
{
$validated_ids = array();
if((strpos($content, '$$range') !== false || strpos($content, '{{range') !== false) && is_array($ids))
{
// Merging into a template that uses range - need ranges, got events
if (is_array($ids) && ($ids[0]['id'] || is_string($ids[0])))
if(is_array($ids) && (is_array($ids[0]) && $ids[0]['id'] || is_string($ids[0])))
{
// Passed an array of events, to be handled like a date range
$events = $ids;
@ -718,8 +787,10 @@ class calendar_merge extends Api\Storage\Merge
'enum_recuring' => true,
'order' => 'cal_start',
'cfs' => strpos($content, '#') !== false ? array_keys(Api\Storage\Customfields::get('calendar')) : null
));
foreach($events as $event) {
)
);
foreach($events as $event)
{
$validated_ids[] = $event;
}
}
@ -749,11 +820,18 @@ class calendar_merge extends Api\Storage\Merge
{
$limits = array('start' => PHP_INT_MAX, 'end' => 0);
$this->ids = array();
foreach($ids as $event) {
foreach($ids as $event)
{
$event = $this->normalize_event_id($event);
if($event['start'] && Api\DateTime::to($event['start'],'ts') < $limits['start']) $limits['start'] = Api\DateTime::to($event['start'],'ts');
if($event['end'] && Api\DateTime::to($event['end'],'ts') > $limits['end']) $limits['end'] = Api\DateTime::to($event['end'],'ts');
if($event['start'] && Api\DateTime::to($event['start'], 'ts') < $limits['start'])
{
$limits['start'] = Api\DateTime::to($event['start'], 'ts');
}
if($event['end'] && Api\DateTime::to($event['end'], 'ts') > $limits['end'])
{
$limits['end'] = Api\DateTime::to($event['end'], 'ts');
}
// Keep ids for future use
if($event['id'])
{
@ -790,7 +868,8 @@ class calendar_merge extends Api\Storage\Merge
'start' => Api\DateTime::to($current, 'ts'),
'end' => Api\DateTime::to($rrule->current(), 'ts') - 1
);
} while ($rrule->valid());
}
while($rrule->valid());
return $validated_ids;
}
@ -809,7 +888,8 @@ class calendar_merge extends Api\Storage\Merge
*/
protected function normalize_event_id($id)
{
if(is_string($id) || is_array($id) && $id['id'] && !$id['start']) {
if(is_string($id) || is_array($id) && $id['id'] && !$id['start'])
{
if(strpos($id, ':'))
{
$_id = $id;
@ -817,7 +897,9 @@ class calendar_merge extends Api\Storage\Merge
list($id['id'], $id['recur_date']) = explode(':', $_id);
}
$event = $this->bo->read(is_array($id) ? $id['id'] : $id, is_array($id) ? $id['recur_date'] : null);
} else {
}
else
{
$event = $id;
}
@ -862,14 +944,21 @@ class calendar_merge extends Api\Storage\Merge
'calendar_owner' => lang('Owner'),
) as $name => $label)
{
if (in_array($name,array('start','end')) && $n&1) // main values, which should be in the first column
if(in_array($name, array('start',
'end')) && $n & 1) // main values, which should be in the first column
{
echo "</tr>\n";
$n++;
}
if (!($n&1)) echo '<tr>';
if(!($n & 1))
{
echo '<tr>';
}
echo '<td>{{' . $name . '}}</td><td>' . $label . '</td>';
if ($n&1) echo "</tr>\n";
if($n & 1)
{
echo "</tr>\n";
}
$n++;
}
@ -923,7 +1012,8 @@ class calendar_merge extends Api\Storage\Merge
}
echo '</table></td><td colspan="2"><table >';
echo '<tr><td><h3>' . lang('Daily tables') . ":</h3></td></tr>";
foreach(self::$relative as $value) {
foreach(self::$relative as $value)
{
echo '<tr><td>{{table/' . $value . '}} ... {{endtable}}</td></tr>';
}
echo '<tr><td>{{table/day_n}} ... {{endtable}}</td><td>1 <= n <= 31</td></tr>';
@ -943,4 +1033,106 @@ class calendar_merge extends Api\Storage\Merge
echo $GLOBALS['egw']->framework->footer();
}
/**
* Get a list of placeholders provided.
*
* Placeholders are grouped logically. Group key should have a user-friendly translation.
*/
public function get_placeholder_list($prefix = '')
{
$placeholders = array(
'event' => [],
'range' => [],
'participant' => [],
'customfields' => []
) + parent::get_placeholder_list($prefix);
unset($placeholders['placeholders']);
$fields = array(
'calendar_id' => lang('Calendar ID'),
'calendar_title' => lang('Title'),
'calendar_description' => lang('Description'),
'calendar_location' => lang('Location'),
'calendar_start' => lang('Start') . ': ' . lang('Date') . '+' . lang('Time'),
'calendar_startday' => lang('Start') . ': ' . lang('Weekday'),
'calendar_startdate' => lang('Start') . ': ' . lang('Date'),
'calendar_starttime' => lang('Start') . ': ' . lang('Time'),
'calendar_end' => lang('End') . ': ' . lang('Date') . '+' . lang('Time'),
'calendar_endday' => lang('End') . ': ' . lang('Weekday'),
'calendar_enddate' => lang('End') . ': ' . lang('Date'),
'calendar_endtime' => lang('End') . ': ' . lang('Time'),
'calendar_duration' => lang('Duration'),
'calendar_category' => lang('Category'),
'calendar_priority' => lang('Priority'),
'calendar_updated' => lang('Updated'),
'calendar_recur_type' => lang('Repetition'),
'calendar_access' => lang('Access') . ': ' . lang('public') . ', ' . lang('private'),
'calendar_owner' => lang('Owner'),
);
$group = 'event';
foreach($fields as $name => $label)
{
$marker = $this->prefix($prefix, $name, '{');
if(!array_filter($placeholders, function ($a) use ($marker)
{
return array_key_exists($marker, $a);
}))
{
$placeholders[$group][] = [
'value' => $marker,
'label' => $label
];
}
}
/**
* These ones only work if you select a range, not events
* $group = 'range';
* foreach(array_keys(self::$range_tags) as $name)
* {
* $marker = $this->prefix($prefix, "range/$name", '{');
* $placeholders[$group][] = [
* 'value' => $marker,
* 'label' => lang($name)
* ];
* }
*/
$group = 'participant';
$placeholders[$group][] = array(
'value' => '{{calendar_participants}}',
'label' => lang('Participants')
);
$placeholders[$group][] = array(
'value' => '{{participant_emails}}',
'label' => 'Emails',
'title' => lang('A list of email addresses of all participants who have not declined')
);
$placeholders[$group][] = array(
'value' => '{{participant_summary}}',
'label' => 'participant summary',
'title' => lang('Summary of participant status: 3 Participants: 1 Accepted, 2 Unknown')
);
$placeholders[$group][] = array(
'value' => '{{calendar_participants/account}}',
'label' => lang('Accounts')
);
$placeholders[$group][] = array(
'value' => '{{calendar_participants/group}}',
'label' => lang('Groups')
);
foreach($this->bo->resources as $resource)
{
if($resource['type'])
{
$marker = $this->prefix($prefix, 'calendar_participants/' . $resource['app'], '{');
$placeholders[$group][] = array(
'value' => $marker,
'label' => lang($resource['app'])
);
}
}
return $placeholders;
}
}

View File

@ -33,8 +33,7 @@ class filemanager_merge extends Api\Storage\Merge
/**
* Fields that are numeric, for special numeric handling
*/
protected $numeric_fields = array(
);
protected $numeric_fields = array();
/**
* Fields that are dates or timestamps
@ -152,12 +151,14 @@ class filemanager_merge extends Api\Storage\Merge
// Try this first - a normal path /apps/appname/id/file
list($app, $app_id) = explode('/', substr($file['path'], strpos($file['path'], 'apps/') + 5));
// Symlink?
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps'])) {
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps']))
{
// Try resolving just app + ID - /apps/App Name/Record Title/file
$resolved = Vfs::resolve_url_symlinks(implode('/', array_slice(explode('/', $file['dir']), 0, 4)));
list($app, $app_id) = explode('/', substr($resolved, strpos($resolved, 'apps/') + 5));
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps'])) {
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps']))
{
// Get rid of any virtual folders (eg: All$) and symlinks
$resolved = Vfs::resolve_url_symlinks($file['path']);
list($app, $app_id) = explode('/', substr($resolved, strpos($resolved, 'apps/') + 5));
@ -181,7 +182,8 @@ class filemanager_merge extends Api\Storage\Merge
}
}
// Silently discard & continue
catch(Exception $e) {
catch (Exception $e)
{
unset($e); // not used
}
}
@ -239,14 +241,18 @@ class filemanager_merge extends Api\Storage\Merge
{
return $session;
}
else if (($session = \EGroupware\Api\Cache::getSession(Api\Sharing::class, "$app::$id")) &&
else
{
if(($session = \EGroupware\Api\Cache::getSession(Api\Sharing::class, "$app::$id")) &&
substr($session['share_path'], -strlen($path)) === $path)
{
return $session;
}
}
// Need to create the share here.
// 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();
$extra = array();
@ -322,4 +328,48 @@ class filemanager_merge extends Api\Storage\Merge
echo $GLOBALS['egw']->framework->footer();
}
/**
* Get a list of placeholders provided.
*
* Placeholders are grouped logically. Group key should have a user-friendly translation.
*/
public function get_placeholder_list($prefix = '')
{
$placeholders = parent::get_placeholder_list($prefix);
$fields = array(
'name' => 'name',
'path' => 'Absolute path',
'rel_path' => 'Path relative to current directory',
'folder' => 'Containing folder',
'folder_file' => 'Containing folder and file name',
'url' => 'url',
'webdav_url' => 'External path using webdav',
'link' => 'Clickable link to file',
'comment' => 'comment',
'mtime' => 'modified',
'ctime' => 'created',
'mime' => 'Type',
'hsize' => 'Size',
'size' => 'Size (in bytes)',
);
$group = 'placeholders';
foreach($fields as $name => $label)
{
$marker = $this->prefix($prefix, $name, '{');
if(!array_filter($placeholders, function ($a) use ($marker)
{
return array_key_exists($marker, $a);
}))
{
$placeholders[$group][] = [
'value' => $marker,
'label' => $label
];
}
}
return $placeholders;
}
}