* @copyright 2009 Alexandros Vellis * @package plugins * @subpackage avelsieve */ /** * Condition for 'date / time' feature. * * This class actually accomodates for both of the two available date/time tests, * according to RFC 5260. * * (Paragraph 4) date test * In UI, this is positioned as follows: * rule['cond']['kind'] = 'message' * rule['cond']['type'] = 'datetime' * rule['cond']['header'] = 'date', 'received' etc. (from method datetime_header_ui) * rule['cond'][...] -> rest of datetime options (so-called "common UI") * * (Paragraph 5) currentdate test * In UI, this is positioned as follows: * rule['cond']['kind'] = 'datetime' * rule['cond'][...] -> rest of datetime options (so-called "common UI") * */ class avelsieve_condition_datetime extends avelsieve_condition { /** * The "ui_tree" variable describes how the user interface is structured. * Each "varname" array key represents an HTML input widget. */ public $ui; /* * @var string Which test to use / build UI for? 'currentdate' or 'date'? */ public $test; /** * Constructor, sets up localized variables of the structures that define * the various date/time options (properties $this->ui) * * @param object $s * @param array $rule * @param integer $n * @param string $test Which test to use / build UI for? 'currentdate' or 'date' * @return void */ function __construct(&$s, $rule, $n, $test = 'currentdate') { parent::__construct($s, $rule, $n); if($test == 'currentdate') { $this->test = 'currentdate'; } else { $this->test = 'date'; } /* TODO - add more headers in here or make this extensible. */ $this->date_headers = array( 'date' => _("Date"), 'received' => _("Received") ); $this->tpl_date_metrics = $tpl_date_metrics = array( 'year' => _("Year"), 'month' => _("Month"), 'day' => _("Day"), 'weekday' => _("Weekday"), 'hour' => _("Hour"), 'minute' => _("Minute"), 'second' => _("Second"), ); $tpl_weekdays = array( '0' => _("Sunday"), '1' => _("Monday"), '2' => _("Tuesday"), '3' => _("Wednesday"), '4' => _("Thursday"), '5' => _("Friday"), '6' => _("Saturday"), ); $this->tpl_date_condition = $tpl_date_condition = array( 'is' => _("Is"), 'le' => _("Before (<=)"), 'ge' => _("After (=>)"), 'lt' => _("Before (<)"), 'gt' => _("After (>)"), ); $tpl_cond_2 = $tpl_date_condition; // This could be separate to allow for more complex conditions, like // regex matches etc. // 'matches' => _("Matches"), $tpl_months = array( '01' => _("January"), '02' => _("February"), '03' => _("March"), '04' => _("April"), '05' => _("May"), '06' => _("June"), '07' => _("July"), '08' => _("August"), '09' => _("September"), '10' => _("October"), '11' => _("November"), '12' => _("December"), ); $this->ui['datetype'] = array( 'name' => 'datetype', 'input' => 'select', 'values' => array( 'occurence' => _("Occurence"), 'specific_date' => _("Specific Date"), 'specific_time' => _("Specific Time"), // 'specific_date_time' => _("Specific Date and Time"), ), 'children' => array( 'occurence' => 'occurence_metric', 'specific_date' => 'specific_date_conditional', 'specific_time' => 'specific_time_conditional', // 'specific_date_time' => 'specific_date_time_conditional', ), ); // Specific Date $this->ui['specific_date_conditional'] = array( 'input' => 'select', 'values' => $tpl_date_condition, 'children' => array() ); foreach($tpl_date_condition as $key => $val) { $this->ui['specific_date_conditional']['children'][$key] = 'specific_date_picker'; } $this->ui['specific_date_picker'] = array( 'input' => 'datepicker', 'input_options' => 'date', 'terminal' => true, ); // Specific Time $this->ui['specific_time_conditional'] = array( 'input' => 'select', 'values' => $tpl_date_condition, 'children' => array() ); foreach($tpl_date_condition as $key => $val) { $this->ui['specific_time_conditional']['children'][$key] = 'specific_time_picker'; } $this->ui['specific_time_picker'] = array( 'input' => 'datepicker', 'input_options' => 'time', 'terminal' => true, ); // Specific Date and Time /* $this->ui['specific_date_time_conditional'] = array( 'input' => 'select', 'values' => $tpl_date_condition, 'children' => array() ); foreach($tpl_date_condition as $key => $val) { $this->ui['specific_date_time_conditional']['children'][$key] = 'specific_date_time_picker'; } $this->ui['specific_date_time_picker'] = array( 'input' => 'datepicker', 'input_options' => 'datetime', 'terminal' => true, ); */ // Occurences $this->ui['occurence_metric'] = array( 'input' => 'select', 'values' => $tpl_date_metrics, 'children' => array(), ); foreach($tpl_date_metrics as $k => $v) { $this->ui['occurence_metric']['children'][$k] = $k.'_occurence_conditional'; $this->ui[$k.'_occurence_conditional'] = array( 'input' => 'select', 'values' => $tpl_cond_2, 'children' => array() ); foreach($tpl_cond_2 as $k2 => $v2) { $this->ui[$k.'_occurence_conditional']['children'][$k2] = 'occurence_'.$k; } $this->ui['occurence_'.$k] = array( 'input' => 'text', 'terminal' => true, 'children' => array() ); } $this->ui['occurence_month']['input'] = 'select'; $this->ui['occurence_month']['values'] = $tpl_months; $this->ui['occurence_day']['input'] = 'select'; $this->ui['occurence_day']['values'] = $this->_rangePadded(1, 31); $this->ui['occurence_weekday']['input'] = 'select'; $this->ui['occurence_weekday']['values'] = $tpl_weekdays; $this->ui['occurence_hour']['input'] = 'select'; $this->ui['occurence_hour']['values'] = $this->_rangePadded(0, 23); $this->ui['occurence_minute']['input'] = 'select'; $this->ui['occurence_minute']['values'] = $this->_rangePadded(0, 59); $this->ui['occurence_second']['input'] = 'select'; $this->ui['occurence_second']['values'] = $this->_rangePadded(0, 60); } public function datetime_header_ui() { $out = ' ' . sprintf ( _("of header %s"), avelsieve_html::generic_listbox('cond['.$this->n.'][header]', $this->date_headers, (isset($this->data['header']) ? $this->data['header'] : '') ) ) . ' '; /* Index extension placeholder */ return $out; } /** * @return string */ public function datetime_common_ui() { $out = $this->ui_tree_output(); return $out; } /** * * @param $varname string Name of input element from which to start off * @param $varvalue string Value of this input element. * @return string */ public function ui_tree_output($varname = '', $varvalue = '') { if(!empty($varname) && !empty($varvalue)) { $k = $this->_getChildOf($varname, $varvalue); } else { $k = 'datetype'; } $out = ''; if(!empty($k)) { $out .= $this->_printWidgetHtml($k); } return $out; } private function _printWidgetHtml($k, $selected = '') { $u = &$this->ui[$k]; $out = ''; switch($u['input']) { case 'select': $out .= ''; break; case 'datepicker': // Note: there is an issue with autocomplete in firefox occasionally overwriting the datepicker // javascript widget. That's why we disable autocomplete according to: // https://developer.mozilla.org/en/How_to_Turn_Off_form_Autocompletion $out .= ''; break; case 'text': $out .= ''; break; default: $out .= ' nothing '; break; } $out .= ''; $out .= ''; // Print the inner UI if we are showing a rule that already has data in it. if(isset($this->data[$k]) && !empty($this->data[$k])) { $out .= $this->ui_tree_output($k, $this->data[$k]); } $out .= ''; return $out; } function _getChildOf($varname, $varvalue) { if(isset($this->ui[$varname]['children'])) { foreach($this->ui[$varname]['children'] as $child => $widget) { if($varvalue == $child) { return $widget; } } } return false; } /** * Like range(), except that it pads array keys to the same string length * by adding leading zeros. * * @param int $start * @param int $end * @param int $step * @return array */ private function _rangePadded($start, $end, $step = 1) { $aNormal = range($start, $end, $step); $length = strlen( $end ); $aPadded = $aNormal; foreach($aPadded as &$val) { if($difference = $length - strlen($val)) { if($difference == 1) $pad = '0'; if($difference == 2) $pad = '00'; if($difference == 3) $pad = '000'; $val = $pad.$val; } } $out = array_combine($aPadded, $aNormal); return $out; } /** * Generate Sieve code and human-readable texts. * * @return array ($out, $text, $terse) */ public function generate_sieve() { $c = &$this->data; $out = $text = $terse = ''; $out .= ' ' . $this->test . ' '; if($this->test == 'currentdate') { $text .= _("Current date / time:") . ' '; $terse .= '' . _("Current date:") . ' '; } elseif($this->test == 'date') { $text .= _("message header"); $terse .= '' . _("Message header") . ''; } if(isset($c['originalzone']) && $c['originalzone']) { $out .= ':originalzone '; } elseif(isset($c['zone'])) { $out .= ':zone '.$c['zone'].' '; } if($c['datetype'] == 'specific_date') { $cmp = &$c['specific_date_conditional']; } elseif($c['datetype'] == 'specific_time') { $cmp = &$c['specific_time_conditional']; } elseif($c['datetype'] == 'occurence') { $cmp = &$c[$c['occurence_metric'].'_occurence_conditional']; } // The human-readable texts for the comparators are deferred because we'll // print them out after showing on what message header they apply to, if // applicable. switch($cmp) { case 'is': case 'on': default: $out .= ':is'; $textDeferred = _("is %s"); $terseDeferred = ' ' . _("is") . ' '; break; case 'le': $out .= ':value "le"'; $textDeferred = _("is before than %s (inclusive)"); $terseDeferred = ' '.$this->tpl_date_condition[$cmp].' '; break; case 'ge': $out .= ':value "ge"'; $textDeferred = _("is after than %s (inclusive)"); $terseDeferred = ' '.$this->tpl_date_condition[$cmp].' '; break; case 'lt': $out .= ':value "lt"'; $textDeferred = _("is before than %s"); $terseDeferred = ' '.$this->tpl_date_condition[$cmp].' '; break; case 'gt': $out .= ':value "gt"'; $textDeferred = _("is after than %s"); $terseDeferred = ' '.$this->tpl_date_condition[$cmp].' '; break; } $out .= ' '; $text .= ' '; $terse .= ' '; /* --- header-name --- (only for date test, not for currentdate). Of course, * for date test, this is required. */ if(!empty($c['header'])) { $out .= '"'.strtolower($c['header']).'" '; $text .= ' '. sprintf( _(""%s""), $this->date_headers[$c['header']] ); $terse .= ' '.sprintf( _(""%s:""), $this->date_headers[$c['header']] ) . '
'; } /* --- date-part + key-list --- */ if($c['datetype'] == 'specific_date') { // From 'specific date' UI $out .= '"date" "'.$c['specific_date_picker'].'"'; $text .= sprintf($textDeferred, htmlspecialchars($c['specific_date_picker'])); $terse .= $terseDeferred . htmlspecialchars($c['specific_date_picker']); } elseif($c['datetype'] == 'specific_time') { // From 'specific time' UI $out .= '"time" "'.$c['specific_time_picker'].':00"'; $text .= sprintf($textDeferred, htmlspecialchars($c['specific_time_picker'])); $terse .= $terseDeferred . htmlspecialchars($c['specific_time_picker']); } elseif($c['datetype'] == 'occurence') { // From 'occurence' UI $out .= '"'.$c['occurence_metric'].'" '; $text .= $this->tpl_date_metrics[$c['occurence_metric']] . ' '; $terse .= $this->tpl_date_metrics[$c['occurence_metric']] . ' '; if(isset($c['occurence_'.$c['occurence_metric']])) { $out .= '"'.$c['occurence_'.$c['occurence_metric']].'" '; } else { //key occurence_'.$c['occurence_metric'] is empty? $out .= '""'; } if(isset($this->ui['occurence_'.$c['occurence_metric']]['values'][$c['occurence_'.$c['occurence_metric']]])) { $humanReadableValue = $this->ui['occurence_'.$c['occurence_metric']]['values'][$c['occurence_'.$c['occurence_metric']]]; } else { $humanReadableValue = $c['occurence_'.$c['occurence_metric']]; } $text .= sprintf($textDeferred, $humanReadableValue); $terse .= $terseDeferred . $humanReadableValue; } return array($out, $text, $terse); } }