protected function runManagerAction($action) { switch ($action) { case 'phpinfo': ob_start(); phpinfo(); $this->content .= ob_get_clean(); return true; case 'clear': if (is_null($dir = TIP::getGet('id', 'string'))) { TIP::warning("GET not found ({$id})"); TIP::notifyError('noparams'); return false; } $dir = TIP::buildDataPath(urldecode($dir)); TIP::removeDir($dir, false); TIP::notifyInfo('done'); return true; } return null; }
/** * Process an add action * * Overrides the default callback providing email notification * when requested. * * @param array &$row The subject row * @param array|null $old_row The old row or null on no old row * @return bool true on success, false on errors */ public function _onAdd(&$row, $old_row) { if (!parent::_onAdd($row, $old_row)) { return false; } if (empty($this->notify_to)) { // No notification required return true; } $eol = "\r\n"; $env = 'TiP-' . TIP_VERSION_BRANCH; $sys = 'PHP-' . phpversion(); $headers = 'From: ' . $this->_getServerEmail() . $eol; $headers .= 'X-Mailer: TIP_Request module ' . "({$env}; {$sys})" . $eol; $headers .= 'MIME-Version: 1.0' . $eol; $headers .= 'Content-Type: text/plain; charset=ISO-8859-1'; // Assign current_row, so getField() checks for values in this row $this->_current_row =& $row; ob_start(); if ($this->tryRun($this->message_template)) { $message = ob_get_clean(); } elseif (array_key_exists($this->message_template, $row)) { ob_end_clean(); $message = $row[$this->message_template]; } else { ob_end_clean(); $message = 'Undefined message'; } $message = wordwrap(utf8_decode($message), 66); if (!mail($this->notify_to, $this->subject_text, $message, $headers)) { TIP::warning("Unable to send an email message to {$this->notify_to}"); return false; } return true; }
/** * Execute a template file * * Parses and executes a template. * * @param TIP_Template &$template The template to run * @param TIP_Module &$caller The caller module * @return bool true on success or false on errors */ public function run(&$template, &$caller) { $path =& $template->getProperty('path'); // Check for cached result if ($this->caching) { $cache = implode(DIRECTORY_SEPARATOR, array_merge($this->cache_root, $path)); if (is_readable($cache)) { return readfile($cache) !== false; } } // Check for compiled file if ($this->compiling) { $compiled = implode(DIRECTORY_SEPARATOR, array_merge(array('.'), $this->compiled_root, $path)) . '.php'; if (is_readable($compiled)) { return (include $compiled) !== false; } } // No cache or compiled file found: parse and run this template $file = $template->__toString(); isset($this->extension) && ($file .= $this->extension); isset($template->_buffer) || ($template->_buffer = file_get_contents($file)); if ($template->_buffer === false) { TIP::error("unable to read file ({$file})"); return false; } ob_start(); $result = $this->runBuffer($template, $caller); if (is_string($result)) { ob_end_clean(); TIP::error($result); return false; } if ($result == false && isset($cache)) { // Caching requested $dir = dirname($cache); if (is_dir($dir) || mkdir($dir, 0777, true)) { file_put_contents($cache, ob_get_contents(), LOCK_EX); } else { TIP::warning("Unable to create the cache path ({$dir})"); } } elseif (isset($compiled)) { // Compiling requested $result = $this->compileBuffer($template); if (is_string($result)) { // Compilation successfull $dir = dirname($compiled); if (is_dir($dir) || mkdir($dir, 0777, true)) { file_put_contents($compiled, $result, LOCK_EX); } else { TIP::warning("Unable to create the compiled path ({$dir})"); } } } ob_end_flush(); return true; }
private function _addCustomRules($id) { $text = @$this->fields[$id]['rules']; if (empty($text)) { return; } $rules = explode(',', $text); foreach ($rules as $rule) { $open_brace = strpos($rule, '('); if ($open_brace === false) { $type = $rule; $format = ''; } else { $close_brace = strrpos($rule, ')'); if ($close_brace === false || $close_brace < $open_brace) { TIP::warning("invalid custom rule for field {$id} ({$rule})"); continue; } $type = substr($rule, 0, $open_brace); $format = substr($rule, $open_brace + 1, $close_brace - $open_brace - 1); if (strpos($format, ' ')) { $format = explode(' ', $format); } } $this->_addRule($id, $type, $format); } }
/** * Check if a row is not owned by the current user * * Similar to isOwner(), but works in the reverse way: check if the row * identified by $id is NOT owned by the current user. * * @param mixed $id The row id * @return bool true if not owned by the current user or false on errors */ public function isNotOwner($id = null) { if (is_null($row =& $this->fromRow($id))) { return false; } if ($this->_isOwner($row) === true) { TIP::warning("owned row ({$id})"); TIP::notifyError('denied'); return false; } return true; }
private function _updateCount($id, $offset) { if (empty($this->count_field)) { return true; } // Global query (probably cached) if (is_null($view =& $this->startDataView())) { TIP::notifyError('select'); return false; } $rows =& $view->getProperty('rows'); $this->endView(); if (!isset($rows[$id])) { TIP::warning("row not found ({$id})"); TIP::notifyError('notfound'); return false; } $old_row =& $rows[$id]; $row[$this->count_field] = $old_row[$this->count_field] + $offset; if (!$this->data->updateRow($row, $old_row)) { TIP::notifyError('update'); return false; } $old_row[$this->count_field] += $offset; return true; }
private function _mapType(&$field, $type) { // Fallback values $field['type'] = 'string'; $field['widget'] = null; $field['length'] = 0; switch (strtoupper($type)) { case 'BOOL': case 'BOOLEAN': $field['type'] = 'bool'; $field['widget'] = 'set'; break; case 'BIT': case 'TINYINT': case 'SMALLINT': case 'MEDIUMINT': case 'INT': case 'INTEGER': case 'BIGINT': $field['type'] = 'int'; break; case 'FLOAT': case 'DOUBLE': case 'DOUBLE PRECISION': case 'REAL': case 'DECIMAL': case 'DEC': case 'NUMERIC': case 'FIXED': $field['type'] = 'float'; break; case 'STRING': case 'CHAR': case 'VARCHAR': case 'BINARY': case 'VARBINARY': $field['type'] = 'string'; break; case 'TINYBLOB': case 'TINYTEXT': $field['type'] = 'string'; $field['widget'] = 'textarea'; $field['length'] = 255; break; case 'BLOB': case 'TEXT': $field['type'] = 'string'; $field['widget'] = 'textarea'; $field['length'] = 65535; break; case 'MEDIUMBLOB': case 'MEDIUMTEXT': $field['type'] = 'string'; $field['widget'] = 'textarea'; $field['length'] = 16777215; break; case 'LONGBLOB': case 'LONGTEXT': $field['type'] = 'string'; $field['widget'] = 'textarea'; $field['length'] = 4294967295.0; break; case 'ENUM': $field['type'] = 'string'; $field['widget'] = 'enum'; break; case 'SET': $field['type'] = 'string'; $field['widget'] = 'set'; break; case 'DATE': $field['type'] = 'string'; $field['widget'] = 'date'; $field['length'] = 10; break; case 'TIME': $field['type'] = 'string'; $field['widget'] = 'time'; $field['length'] = 8; break; case 'DATETIME': $field['type'] = 'string'; $field['widget'] = 'datetime'; $field['length'] = 19; break; case 'TIMESTAMP': $field['type'] = 'int'; $field['widget'] = 'datetime'; break; case 'YEAR': $field['type'] = 'string'; $field['length'] = 4; break; default: $field['type'] = 'string'; TIP::warning("field type not supported ({$type})"); } }
/** * Update the history on a master row deletion * * Updates the linked list by skipping the deleted history row * before deleting the row itsself. */ public function _onMasterDelete(&$row, $old_row) { $master_data =& $this->master->getProperty('data'); $id = $row[$master_data->getProperty('primary_key')]; $engine =& $this->data->getProperty('engine'); $query = $this->data->rowFilter($id); // Start the transaction here to avoid race conditions if (!$engine->startTransaction()) { // This error must be caught here to avoid the rollback return false; } // Get the current version row if (!($view =& $this->startDataView($query))) { $engine->endTransaction(false); return false; } $current_row = $view->current(); $this->endView(); if (empty($current_row)) { // No history found: return operation done (just in case...) return $engine->endTransaction(true); } // Get the previous version row $query = $this->data->filter($this->next_field, $id); if (!($view =& $this->startDataView($query))) { $engine->endTransaction(false); TIP::warning("no row to delete ({$id})"); TIP::notifyError('notfound'); return false; } $previous_row = $view->current(); $this->endView(); // Perform the operations $done = $this->data->deleteRow($id); if ($done && is_array($previous_row)) { // Update the next_field of previous_row $new_previous_row = $previous_row; $new_previous_row[$this->next_field] = $current_row[$this->next_field]; $done = $this->data->updateRow($new_previous_row, $previous_row); } // Close the transaction $done = $engine->endTransaction($done) && $done; return $done; }
/** * Get a localized text * * Gets the localized text for a specified module. $id is prefixed by the * 'locale_prefix' property. * * This method always returns a valid string: if the localized text * can't be retrieved, a string containing prefix.$id is returned and a * warning message is logged. * * See the TIP_Locale::get() method for technical details on how the text * is localized. * * @param string $id The identifier * @param array $context A context associative array * @param bool $cached Whether to perform or not a cached read * @return string The requested localized text */ protected function getLocale($id, $context = null, $cached = true) { $text = TIP::getLocale($id, $this->locale_prefix, $context, $cached); if (empty($text)) { $text = $this->locale_prefix . '.' . $id; TIP::warning("localized text not found ({$text})"); } return $text; }
/** * Process a delete action * * Overrides the default delete action erasing both master and child rows. */ public function _onDelete(&$row, $old_row) { $child =& $this->_getChildModule(); if (is_null($child)) { // No child module: chain-up the parent method return parent::_onDelete($row, $old_row); } elseif (!$child) { // An error occurred somewhere: do nothing return false; } $primary_key = $this->data->getProperty('primary_key'); if (!array_key_exists($primary_key, $row) || is_null($id = $row[$primary_key])) { TIP::warning("no primary key defined (field {$primary_key})"); return false; } $engine =& $this->data->getProperty('engine'); if (!$engine->startTransaction()) { // This error must be caught here to avoid the rollback return false; } $done = parent::_onDelete($row, $old_row) && $child->getProperty('data')->deleteRow($id); $done = $engine->endTransaction($done) && $done; return $done; }
public function cancelTransaction() { TIP::warning('cancelTransaction()'); return true; }
public function &select(&$data, $filter, $fields) { // Always work on a copy of the cached rows $rows = $this->_getRows($data, $fields); if (empty($rows)) { return $rows; } if (!empty($filter)) { // Provide basic SQL filtering if (!preg_match('"^\\s*(?:WHERE\\s+(\\S+?)\\s*(=|<>|>|<)\\s*(\\S+))?\\s*(?:LIMIT\\s*(\\d+)(?:\\s*,\\s*(\\d+))?)?\\s*$"i', $filter, $matches)) { // Unrecognized query: raise a warning and returns an empty set TIP::warning("query not recognized ({$filter})"); $rows = array(); return $rows; } $field = @$matches[1]; $operator = @$matches[2]; $value = @$matches[3]; $length = @$matches[4]; $offset = @$matches[5]; // Apply the WHERE clause if (!empty($field)) { // Change the assignment to the (expected) comparison operator $operator == '=' && ($operator = '=='); // Array filtering $callback = create_function('$row', "return \$row['{$field}']{$operator}'{$value}';"); $rows = array_filter($rows, $callback); } // Apply the LIMIT clause if (!empty($length)) { // array reduction (the LIMIT clause) isset($offset) || ($offset = 0); $rows = array_slice($rows, $offset, $length); } } return $rows; }
private function _castField(&$value, $key) { if (array_key_exists($key, $this->_fields)) { $field =& $this->_fields[$key]; if ((!is_null($value) || !@$field['can_be_null']) && !settype($value, $field['type'])) { TIP::warning("invalid type for field['{$key}'] => {$value} ({$field['type']})"); } } }
/** * Get the timestamp from a special date * * Parses $date, specified in $format format, and return the timestamp. * The currently supported formats are: * - 'sql' for SQL date or datetime (YYYY-MM-DD hh:mm:ss) * * @param mixed $date The input date * @param string $format A supported date format * @return int|null $date converted in timestamp or null on errors */ public static function getTimestamp($date, $format) { switch ($format) { case 'sql': @(list($year, $month, $day, $hour, $min, $sec) = sscanf($date, '%d-%d-%d %d:%d:%d')); return mktime($hour, $min, $sec, $month, $day, $year); } TIP::warning("Input time format not recognized ({$format})"); return null; }
private function _renderField($field, &$row) { if (is_string($field)) { return $row[$field]; } elseif (is_array($field)) { return implode(', ', array_intersect_key($row, array_flip($field))); } // No way to render this field $field_type = gettype($field); TIP::warning("not a valid field type ({$field_type})"); return ''; }
/** * Get the privilege level from a privilege id * * The reverse operation of tagPrivilegeId(). */ protected function tagPrivilegeLevel($params) { $level = array_search($params, $this->_privileges); if ($level === false) { TIP::warning("undefined privilege id ({$params})"); return null; } return $level; }