/** * Creates an instance of the appropriate action class * then invokes the "execute" method on the instance. An optional * framework parameter may be delegated to the action. If * the Action implements a method matching the given task, it * will be invoked rather than the "execute" method. * * The return value from the Action will be passed to a * corresponding Page instance (created in the same manner) via * its "render" method. The given task may override the target * method as described above. * * If the Dispatcher instance is visible in global scope, the Action * implementation may dynamically override the task method for the * subsequent Page invocation as follows: * <pre> * global $dispatcher; * $dispatcher->setTask('pageTask'); * </pre> * * @param String $scope A key for finding the action/page prefix * @param String $task An optional target method to invoke */ public function dispatch($scope, $task = null) { $timer = Horde_Timer::singleton(); $timer->push(); // overall timer for action+page $this->setTask($task); $this->setRedirect(null); $action = $this->getAction($scope); if (!is_subclass_of($action, 'BaseAction')) { trigger_error("Invalid Action class: get_class({$action})", E_USER_ERROR); return; } $bean = null; $method = $this->getTask(); $timer->push(); // timer for action if (!empty($method) && in_array($method, get_class_methods($action))) { $bean = $action->{$method}($this->param); } else { $bean = $action->execute($this->param); } $this->timers[$scope . ':action'] = $timer->pop(); if ($this->getRedirect() == null || $scope == $this->getRedirect() && $task == $this->getTask()) { $page = $this->getPage($scope); if (!is_subclass_of($page, 'BasePage')) { trigger_error("Invalid Page class: get_class({$action})", E_USER_ERROR); return; } $method = $this->getTask(); $timer->push(); // timer for page if (!empty($method) && in_array($method, get_class_methods($page))) { $page->{$method}($bean); } else { $page->render($bean); } $this->timers[$scope . ':page'] = $timer->pop(); } else { $this->dispatch($this->getRedirect(), $this->getTask()); } $this->timers[$scope . ':overall'] = $timer->pop(); Horde::logMessage("Dispatcher timing (ms) for {$scope} [{$this->task}]: " . print_r($this->timers, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); }
/** * Returns a list of beans that matched the criteria * given in the arguments * * @param db A database connection * @param namedBean A template bean identifiying the query table/columns * @param listBean Query parameters and results * @param where optional where clause element(s) * @param values optional values for prepared statement elements * @return boolean true if the query was successful, else false */ public function select(&$db, $namedBean, &$listBean = null, $where = array(), $values = array()) { Horde_Timer::singleton()->push(); if (empty($listBean)) { $listBean = new ListBean(); } $criteria = $listBean->get_criteria(); $currentPage = $listBean->get_current_page(); $pageSize = $listBean->get_page_size(); $orderBy = $listBean->get_order_by(); $cols = $namedBean->get_bean_name() . '.*'; // special handling to build timestamps from date columns // This is done to preserve naming convention for dates // while allowing the application to convert the value to a date OR // timestamp as needed. foreach ($namedBean as $key => $value) { if (strpos(strtolower($key), 'timestamp') > 0) { $cols .= ', TO_CHAR(' . str_replace('timestamp', 'date', $key) . ', \'YYYY-MM-DD HH24:MI:SS\') AS ' . $key; } } foreach ($criteria as $property => $val) { $opcode = '='; $value = $val; if (property_exists(get_class($namedBean), $property) && empty($value)) { Horde::logMessage("NOTE: {$property} has empty value in _criteriaQuery", __FILE__, __LINE__, PEAR_LOG_WARNING); } if (property_exists(get_class($namedBean), $property) && !empty($value)) { if (is_object($value) && get_class($value) == 'Criterion') { $value = $val->get_value(); $opcode = $val->get_operator(); } if (!is_numeric($value) && ($value == 'null' || $value == 'not null')) { $where[] = $property . ' is ' . $value; } else { if (is_array($value)) { $tokens = $delim = ''; foreach ($value as $var) { $tokens .= $delim . '?'; $delim = ','; $values[] = $var; } if (empty($tokens)) { $where[] = '1=0'; } else { if ($opcode == 'between' && count($value) == 2) { $where[] = "{$property} between ? and ?"; } else { $where[] = $property . ' in (' . $tokens . ')'; } } } else { if (strpos(strtolower($property), 'timestamp') > 0) { $where[] = $property . ' ' . $opcode . ' TO_DATE(?, \'YYYY-MM-DD HH24:MI:SS\')'; $values[] = date('Y-m-d H:i:s', strtotime($value)); } else { if (strpos(strtolower($property), 'date') > 0) { $where[] = 'trunc(' . $property . ') ' . $opcode . ' TO_DATE(?, \'YYYY-MM-DD\')'; $values[] = date('Y-m-d', strtotime($value)); } else { if (is_string($value) && !is_numeric($value) && $opcode == '=') { $where[] = $property . ' like ?'; $values[] = '%' . trim($value) . '%'; } else { $where[] = $property . ' ' . $opcode . ' ?'; $values[] = $value; } } } } } } } $sql = 'SELECT ' . $cols . ' FROM ' . $namedBean->get_bean_name() . (count($where) ? ' WHERE ' . join(' AND ', $where) : ''); $record_count = $listBean->get_record_count(); if ($record_count == 0) { $csql = str_replace($cols, 'COUNT(*) AS count', $sql); Horde::logMessage($csql . "\n" . print_r($values, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); $result = $db->query($csql, $values); if (is_a($result, 'PEAR_Error')) { $listBean->setError($result); QueryUtil::handleError($result, $csql, $values, __LINE__); return false; } $row = $result->fetchRow(DB_FETCHMODE_ASSOC); if (is_a($row, 'PEAR_Error')) { $listBean->setError($row); QueryUtil::handleError($row, $csql, $values, __LINE__); return false; } $record_count = (int) $row['count']; $listBean->set_record_count($record_count); } if ($pageSize < 0) { $pageSize = $record_count; } $firstRow = $currentPage * $pageSize + 1; if ($firstRow <= 0) { $firstRow = 1; $currentPage = 0; } $lastRow = ($currentPage + 1) * $pageSize; if ($lastRow > $record_count) { $lastRow = $record_count; $currentPage = (int) (($record_count - 1) / $pageSize); $firstRow = $currentPage * $pageSize + 1; } $listBean->set_current_page($currentPage); $list = array(); if ($record_count > 0) { global $conf; if ($conf['data']['use_ci_sort']) { foreach ($orderBy as &$sort) { $parts = explode(' ', $sort); $sort = 'NLS_UPPER(' . $parts[0] . ', \'NLS_SORT = GENERIC_BASELETTER\') ' . (count($parts) > 1 ? $parts[1] : ''); } } $psql = 'SELECT * FROM ( SELECT x.*, RowNum AS row_index FROM ( ' . $sql . (count($orderBy) ? ' ORDER BY ' . join(', ', $orderBy) : '') . ' ) x WHERE RowNum <= ' . $lastRow . ' ' . ' ) WHERE row_index >= ' . $firstRow; Horde::logMessage($psql . "\n" . print_r($values, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); $beanClass = get_class($namedBean); $beanKey = $namedBean->get_bean_key(); $resultSet = $db->query($psql, $values); if (is_a($resultSet, 'PEAR_Error')) { $listBean->setError($resultSet); QueryUtil::handleError($resultSet, $psql, $values, __LINE__); return false; } $row = $resultSet->fetchRow(DB_FETCHMODE_ASSOC); while (!empty($row)) { if (is_a($row, 'PEAR_Error')) { $listBean->setError($row); QueryUtil::handleError($row, $psql, $values, __LINE__); return false; } if (is_array($beanKey)) { $keyVars = array(); foreach ($beanKey as $key) { $keyVars[] = $row[$key]; } $listKey = join('|', $keyVars); $list[$listKey] = new $beanClass($row); } else { $list[$row[$beanKey]] = new $beanClass($row); } $row = $resultSet->fetchRow(DB_FETCHMODE_ASSOC); } } else { Horde::logMessage('No matching rows: ' . $sql, __FILE__, __LINE__, PEAR_LOG_INFO); } $listBean->set_list($list); global $dispatcher; $dispatcher->timers[__CLASS__][__FUNCTION__][] = Horde_Timer::singleton()->pop(); return true; }