Пример #1
0
 /**
  * End a transaction
  *
  * End the current transaction. If there's no active transaction,
  * raise an error and return false.
  *
  * The transaction is really ended when the last started transaction
  * is closed. For instance, if you call startTransaction() twice and
  * endTransaction() once, the data will be never committed.
  * 
  * The error flag indicates if the transaction must be rolled back
  * ($commit = true) of committed ($commit = false).
  *
  * @param  bool $commit Commit (if true) or rollback (if false)
  * @return bool         true on success or false on errors
  */
 public function endTransaction($commit)
 {
     if ($this->_transaction_ref == 0) {
         TIP::error('ending a never started transaction');
         return false;
     }
     --$this->_transaction_ref;
     if ($this->_transaction_ref > 0) {
         return true;
     }
     $action = $commit ? TIP_TRANSACTION_COMMIT : TIP_TRANSACTION_ROLLBACK;
     return $this->transaction($action);
 }
Пример #2
0
 /**
  * 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;
 }
Пример #3
0
 /**
  * Perform the browse query throught a pager
  *
  * $params is a string in the form "quanto,query_adds".
  *
  * The quanto is the number of rows per page: leave it undefined to disable
  * the pager. In query_adds you can specify additional SQL commands to
  * append to the query, such as ORDER clauses.
  *
  * This function checks if there is a row more than what specified in the
  * quanto: this provides a simple way to know whether the 'NEXT' button
  * must be rendered or not.
  */
 protected function tagPager($params)
 {
     if (is_null($this->_pager_conditions)) {
         TIP::error('no active browse action');
         return null;
     }
     @(list($quanto, $query_template) = explode(',', $params, 2));
     $quanto = (int) $quanto;
     $pager = $quanto > 0;
     if (empty($this->_pager_conditions)) {
     } elseif (is_array($this->_pager_conditions)) {
         $conditions = array();
         foreach ($this->_pager_conditions as $id => $value) {
             $conditions[] = $this->getData()->addFilter('', $id, $value);
         }
         $filter = 'WHERE (' . implode(' AND ', $conditions) . ')';
     } elseif (empty($this->search_field)) {
         $filter = $this->_pager_conditions;
     } else {
         is_string($this->search_field) && ($this->search_field = explode(',', $this->search_field));
         $this->_search_tokens = explode(' ', $this->_pager_conditions);
         $pattern = '%' . implode('%', $this->_search_tokens) . '%';
         $conditions = array();
         foreach ($this->search_field as $id) {
             $conditions[] = $this->getData()->addFilter('', $id, $pattern, 'LIKE');
         }
         $filter = 'WHERE (' . implode(' OR ', $conditions) . ')';
     }
     if (isset($filter)) {
         $filter .= ' ' . $query_template;
     } else {
         $filter = $query_template;
     }
     $filter .= $this->getData()->order($this->default_order);
     if ($pager) {
         $offset = TIP::getGet('pg_offset', 'int');
         $offset > 0 || ($offset = 0);
         $filter .= $this->getData()->limit($quanto + 1, $offset);
     } else {
         $offset = 0;
     }
     if (is_null($view = $this->startDataView($filter))) {
         TIP::notifyError('select');
         $this->_search_tokens = null;
         return null;
     }
     ob_start();
     if (!$view->isValid()) {
         $this->tryRun(array($main_id, $this->pager_empty_template));
     } else {
         $main_id = TIP_Application::getGlobal('id');
         $partial = $pager && $view->nRows() == $quanto + 1;
         if ($partial) {
             // Remove the trailing row from the view
             $rows =& $view->getProperty('rows');
             array_splice($rows, $quanto);
         }
         if ($pager) {
             if ($offset > 0) {
                 $this->keys['PREV'] = TIP::modifyActionUri(null, null, null, array('pg_offset' => $offset - $quanto > 0 ? $offset - $quanto : 0));
             }
             if ($partial) {
                 $this->keys['NEXT'] = TIP::modifyActionUri(null, null, null, array('pg_offset' => $offset + $quanto));
             }
             $pager = $partial || $offset > 0;
         }
         // Pager rendering BEFORE the rows
         $pager && $this->tryRun(array($main_id, $this->pager_pre_template));
         // Rows rendering
         $empty = true;
         $path = array($this->id, $this->pager_template);
         foreach ($view as $row) {
             $this->run($path);
             $empty = false;
         }
         // Empty result set
         $empty && $this->tryRun(array($main_id, $this->pager_empty_template));
         // Pager rendering AFTER the rows
         $pager && $this->tryRun(array($main_id, $this->pager_post_template));
     }
     $this->endView();
     $this->_search_tokens = null;
     return ob_get_clean();
 }
Пример #4
0
 private function _addRule($id, $type, $format = '')
 {
     // Add the format as context to getLocale (in case the localized message
     // will embed any format field)
     if (is_array($format)) {
         $context = $format;
     } elseif (!empty($format)) {
         $context[0] = $format;
     } else {
         $context = null;
     }
     $message = $this->getLocale('rule.' . $type, $context);
     $result = $this->_form->addRule($id, $message, $type, $format, $this->validation);
     if (PEAR::isError($result)) {
         TIP::error($result->getMessage());
     }
 }
Пример #5
0
 public function query()
 {
     $pieces = func_get_args();
     $query = implode(' ', $pieces);
     $result = mysql_query($query, $this->_connection);
     if ($result === false) {
         TIP::error(mysql_error($this->_connection) . " ({$query})");
     }
     return $result;
 }
Пример #6
0
 /**
  * Echo a row descriptor
  *
  * Outputs the full path (as generated by the row renderer) of the
  * row with $params id.
  */
 protected function tagDescriptor($params)
 {
     if (empty($params)) {
         TIP::error('no row specified');
         return null;
     } elseif (is_null($renderer = $this->_getRenderer())) {
         return null;
     }
     $rows =& $renderer->toArray();
     if (!array_key_exists($params, $rows)) {
         TIP::error("row not found ({$params})");
         return null;
     }
     return $rows[$params];
 }
Пример #7
0
 /**
  * Check if a value is in a list
  *
  * $params is a string in the form "needle,value1,value2,...".
  *
  * Outputs true if needle is present in the comma separated list of values.
  * Useful to check if a value is contained (that is, if it is selected) in
  * a "set" or "enum" field.
  */
 protected function tagInList($params)
 {
     $pos = strpos($params, ',');
     if ($pos === false) {
         TIP::error("invalid inList parameter ({$params})");
         return null;
     }
     $needle = substr($params, 0, $pos);
     $list = explode(',', substr($params, $pos + 1));
     return in_array($needle, $list) ? 'true' : 'false';
 }
Пример #8
0
 /**
  * Get the child module
  *
  * Checks for the child module existence and caches the request.
  * If $class is not specified, no attempts are made to get the
  * child module: only the cache is returned or an error is raised.
  *
  * This method provides also a way to validate the data engine,
  * that **must** be shared between this module and the child one
  * to allow //transation protected// commits.
  *
  * @param  string|null            $class The class to use
  * @return TIP_Content|null|false        The requested child module,
  *                                       null if not needed or
  *                                       false on errors
  * @internal
  */
 private function &_getChildModule($class = null)
 {
     // The true value is used as "uncached" value
     static $child = true;
     // Check for cached result
     if ($child !== true) {
         return $child;
     }
     // Check for request without $class (no autodiscovering)
     if (is_null($class)) {
         TIP::error('No previous child request performed');
         $error = false;
         return $error;
     }
     // Check if the child module is required
     $child = TIP_Type::getInstance($this->id . '-' . $class, false);
     if (is_null($child)) {
         // No child module needed
         return $child;
     }
     // Get and check the child module
     $child_data = $child->getProperty('data');
     if (!$child_data instanceof TIP_Data) {
         TIP::error("the child module has no data (child = {$class})");
         $child = false;
     } elseif ($child_data->getProperty('engine') != $this->data->getProperty('engine')) {
         TIP::error("master and child data must share the same data engine (child = {$class})");
         $child = false;
     }
     return $child;
 }
Пример #9
0
 private function &_getRows(&$data, $fields)
 {
     $path = $data->getProperty('path');
     if (!array_key_exists($path, $this->_rows)) {
         if (strncmp($path, 'http://', 7) == 0) {
             $uri = $path;
             if (function_exists('curl_init')) {
                 // CURL extension available: this should be the
                 // first attempt because the dumb 'open_basedir'
                 // directive can f**k up file_get_contents()
                 $curl = curl_init();
                 curl_setopt($curl, CURLOPT_URL, $uri);
                 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
                 $xml_data = curl_exec($curl);
                 curl_close($curl);
             } else {
                 if (in_array('http', stream_get_wrappers())) {
                     // http wrapper present
                     $xml_data = file_get_contents($uri);
                 } else {
                     // No viable way to use the http protocol
                     $xml_data = false;
                 }
             }
         } else {
             $uri = TIP::buildDataPath($data->getProperty('path'));
             $xml_data = file_get_contents($uri);
         }
         $xml_tree = false;
         if (is_string($xml_data)) {
             // Work-around to let SimpleXML be happy with the f*****g
             // default namespace
             $xml_data = str_replace(' xmlns=', ' fakens=', $xml_data);
             $xml_tree = @simplexml_load_string($xml_data);
         }
         if ($xml_tree) {
             // Takes only the first element matching "base_xpath"
             $xml = reset($xml_tree->xpath($this->base_xpath));
             $this->_data =& $data;
             if (empty($fields)) {
                 $this->_fields = array_keys($this->fields_xpath);
             } else {
                 $this->_fields = $fields;
             }
             $nodes = $xml->xpath($this->row_xpath);
             $rows = $this->_nodesToRows($nodes);
             unset($nodes, $this->_fields, $this->_data);
         } else {
             $rows = array();
             TIP::error("failed to load XML file ({$uri})");
         }
         $this->_rows[$path] = $rows;
     }
     return $this->_rows[$path];
 }
Пример #10
0
 private function _validate(&$row)
 {
     $this->getFields(false);
     // Keep only the keys that are fields
     $row = array_intersect_key($row, $this->_fields);
     if (isset($this->fieldset)) {
         // Apply the fieldset filter
         $row = array_intersect_key($row, array_flip($this->fieldset));
     }
     // Check for empty set
     if (empty($row)) {
         TIP::error('no valid field found');
         return false;
     }
     // Cast the set to the proper type
     $this->_castRow($row);
     return true;
 }
Пример #11
0
 /**
  * Get a localized text
  *
  * Given an $id and a $prefix, retrieves the localized text (in the locale
  * specified by the 'locale' option of the main TIP_Application) binded to
  * "$prefix.$id". This means the data object of TIP_Locale must have a
  * series of rows with prefix.id values as the primary key.
  *
  * The choice of splitting the key in $prefix and $id allows to perform
  * a sort of cached read, that is, to avoid multiple queries, a single
  * request for a specified prefix gets all the id of this prefix.
  * If you are sure the other id are not used (such as for the TIP_Notify
  * module) you can disable the cache by passig false to $cached.
  *
  * The $context associative array contains a series of key=>value pairs
  * that can be substituted in the localized text. The get() method will
  * search in the localized text for any key enclosed by '|' and will put 
  * the corresponding value. For instance, if there is a 'size'=>200 in
  * the $context array, the text 'Max allowed size is |size|...' will
  * expand to 'Max allowed size is 200...'.
  *
  * @param  string      $id      The identifier
  * @param  string      $prefix  The prefix
  * @param  array       $context A context associative array
  * @param  bool        $cached  Whether to perform or not a cached read
  * @return string|null          The localized text or null if not found
  */
 public function get($id, $prefix, $context, $cached)
 {
     $row_id = $prefix . '.' . $id;
     if (array_key_exists($row_id, $this->_cache)) {
         // Localized text found in the TIP_Locale cache
         $row =& $this->_cache[$row_id];
     } elseif ($cached) {
         $filter = $this->data->filter('id', $prefix . '.%', 'LIKE');
         if (is_null($view =& $this->startDataView($filter))) {
             TIP::error("no way to get localized text ({$row_id})");
             return null;
         }
         $this->_cache += $view->getProperty('rows');
         $this->endView();
         if (array_key_exists($row_id, $this->_cache)) {
             $row =& $this->_cache[$row_id];
         } else {
             // $row_id not found
             $this->_cache[$row_id] = null;
             return null;
         }
     } else {
         $row =& $this->data->getRow($row_id);
         if (is_null($this->_cache[$row_id] = $row)) {
             // $row_id not found
             return null;
         }
     }
     $text = $row[$this->locale];
     if (is_null($context) || strpos($text, '|') === false) {
         return $text;
     }
     // There are some embedded keys to expand ...
     $token = explode('|', $text);
     foreach ($token as $n => $value) {
         // Odd tokens are keys
         if ($n & 1) {
             $token[$n] = array_key_exists($value, $context) ? $context[$value] : '';
         }
     }
     return implode($token);
 }
Пример #12
0
 /**
  * 'on_process' callback for refresh actions
  *
  * Posticipates the expiration date using the current date as
  * reference.
  *
  * @param  array &$row The row to update
  * @return bool        true on success or false on errors
  */
 public function _onRefresh(&$old_row)
 {
     if (!isset($this->expiration_field)) {
         // Undefined expiration field: returns true silently
         return true;
     }
     $expiration = strtotime($this->expiration);
     if ($expiration === false) {
         TIP::error("invalid expiration value ({$this->expiration})");
         return false;
     }
     $row[$this->expiration_field] = TIP::formatDate('datetime_sql', $expiration);
     return $this->_onEdit($row, $old_row);
 }