/** * Returns a fully qualified class name for a given identifier. * * @param ObjectIdentifier $identifier An identifier object * @return string|false Return the class name on success, returns FALSE on failure */ public function locate(ObjectIdentifier $identifier) { $class = StringInflector::camelize(implode('_', $identifier->path)) . ucfirst($identifier->name); $package = ucfirst($identifier->package); $name = ucfirst($identifier->name); //Make an exception for 'view' and 'module' types $path = $identifier->path; $type = !empty($path) ? array_shift($path) : ''; if (!in_array($type, array('view', 'module'))) { $path = ucfirst($type) . StringInflector::camelize(implode('_', $path)); } else { $path = ucfirst($type); } //Allow locating default classes if $path is empty. if (empty($path)) { $path = $name; $name = ''; } $result = false; foreach ($this->_fallbacks as $fallback) { $result = str_replace(array('<Package>', '<Path>', '<Name>', '<Class>'), array($package, $path, $name, $class), $fallback); if (!class_exists($result)) { $result = false; } else { break; } } return $result; }
/** * Parse the identifier * * @param ObjectIdentifier $identifier An object identifier * @return array */ public function parseIdentifier(ObjectIdentifier $identifier) { $package = ucfirst($identifier->package); $path = StringInflector::implode($identifier->path); $file = ucfirst($identifier->name); $class = $path . $file; $info = array('class' => $class, 'package' => $package, 'path' => $path, 'file' => $file); return $info; }
/** * Create an entity or a collection instance * * @param ObjectConfigInterface $config A ObjectConfig object with configuration options * @param ObjectManagerInterface $manager A ObjectInterface object * @return EventPublisher */ public static function getInstance(ObjectConfigInterface $config, ObjectManagerInterface $manager) { $name = $config->object_identifier->name; if (StringInflector::isSingular($name)) { $class = __NAMESPACE__ . '\\ModelEntityRow'; } else { $class = __NAMESPACE__ . '\\ModelEntityRowset'; } $instance = new $class($config); return $instance; }
/** * Instantiate the object * * If the behavior is auto mixed also lazy mix it into related row objects. * * @param ObjectConfig $config A ObjectConfig object with configuration options * @param ObjectManagerInterface $manager A ObjectInterface object * @return DatabaseBehaviorAbstract */ public static function getInstance(ObjectConfig $config, ObjectManagerInterface $manager) { $classname = $config->object_identifier->classname; $instance = new $classname($config); //If the behavior is auto mixed also lazy mix it into related row objects. if ($config->auto_mixin) { $identifier = clone $instance->getMixer()->getIdentifier(); $identifier->path = array('database', 'row'); $identifier->name = StringInflector::singularize($identifier->name); $manager->registerMixin($identifier, $instance); } return $instance; }
/** * Instantiate the object * * If the behavior is auto mixed also lazy mix it into related row objects. * * @param ObjectConfigInterface $config A ObjectConfig object with configuration options * @param ObjectManagerInterface $manager A ObjectInterface object * @return DatabaseBehaviorAbstract */ public static function getInstance(ObjectConfigInterface $config, ObjectManagerInterface $manager) { $class = $manager->getClass($config->object_identifier); $instance = new $class($config); //Lazy mix behavior into related row objects. A supported behavior always has one is[Behaviorable] method. if ($instance->isSupported() && $instance->getMixer() && count($instance->getMixableMethods()) > 1) { $identifier = $instance->getMixer()->getIdentifier()->toArray(); $identifier['path'] = array('database', 'row'); $identifier['name'] = StringInflector::singularize($identifier['name']); $manager->registerMixin($identifier, $instance); } return $instance; }
/** * Returns a fully qualified class name for a given identifier. * * @param ObjectIdentifier $identifier An identifier object * @return string|false Return the class name on success, returns FALSE on failure */ public function locate(ObjectIdentifier $identifier) { $class = StringInflector::camelize(implode('_', $identifier->path)) . ucfirst($identifier->name); $package = ucfirst($identifier->package); $path = StringInflector::camelize(implode('_', $identifier->path)); $name = ucfirst($identifier->name); $result = false; foreach ($this->_fallbacks as $fallback) { $result = str_replace(array('<Package>', '<Path>', '<Name>', '<Class>'), array($package, $path, $name, $class), $fallback); if (!class_exists($result)) { $result = false; } else { break; } } return $result; }
/** * Set the referrer * * @param CommandContext $context A command context object * @return void */ public function setReferrer(CommandContext $context) { if (!$context->request->cookies->has('referrer_locked')) { $request = $context->request->getUrl(); $referrer = $context->request->getReferrer(); //Compare request url and referrer if (!isset($referrer) || (string) $referrer == (string) $request) { $controller = $this->getMixer(); $identifier = $controller->getIdentifier(); $option = 'com_' . $identifier->package; $view = StringInflector::pluralize($identifier->name); $referrer = $controller->getView()->getRoute('option=' . $option . '&view=' . $view, true, false); } //Add the referrer cookie $cookie = $this->getObject('lib:http.cookie', array('name' => 'referrer', 'value' => $referrer, 'path' => $context->request->getBaseUrl()->getPath() ?: '/')); $context->response->headers->addCookie($cookie); } }
/** * Return the views output * * This function will auto assign the model data to the view if the auto_assign * property is set to TRUE. * * @return string The output of the view */ public function render() { $model = $this->getModel(); //Auto-assign the state to the view $this->state = $model->getState(); //Auto-assign the data from the model if ($this->_auto_assign) { //Get the view name $name = $this->getName(); //Assign the data of the model to the view if (StringInflector::isPlural($name)) { $this->{$name} = $model->getRowset(); $this->total = $model->getTotal(); } else { $this->{$name} = $model->getRow(); } } return parent::render(); }
/** * Get the list data * * @return array The array with data to be encoded to json */ protected function _getRowset() { //Get the model $model = $this->getModel(); //Get the route $route = $this->getRoute(); //Get the model state $state = $model->getState(); //Get the model paginator $paginator = $model->getPaginator(); $vars = array(); foreach ($state->toArray() as $var) { if (!$var->unique) { $vars[] = $var->name; } } $data = array('version' => '1.0', 'href' => (string) $route->setQuery($state->getValues(), true), 'url' => array('type' => 'application/json', 'template' => (string) $route->toString(HttpUrl::BASE) . '?{&' . implode(',', $vars) . '}'), 'offset' => (int) $paginator->offset, 'limit' => (int) $paginator->limit, 'total' => 0, 'items' => array(), 'queries' => array()); if ($list = $model->getRowset()) { $vars = array(); foreach ($state->toArray() as $var) { if ($var->unique) { $vars[] = $var->name; $vars = array_merge($vars, $var->required); } } $name = StringInflector::singularize($this->getName()); $items = array(); foreach ($list as $item) { $id = $item->getIdentityColumn(); $items[] = array('href' => (string) $this->getRoute('view=' . $name . '&id=' . $item->{$id}), 'url' => array('type' => 'application/json', 'template' => (string) $this->getRoute('view=' . $name) . '?{&' . implode(',', $vars) . '}'), 'data' => $item->toArray()); } $queries = array(); foreach (array('first', 'prev', 'next', 'last') as $offset) { $page = $paginator->pages->{$offset}; if ($page->active) { $queries[] = array('rel' => $page->rel, 'href' => (string) $this->getRoute('limit=' . $page->limit . '&offset=' . $page->offset)); } } $data = array_merge($data, array('total' => $paginator->total, 'items' => $items, 'queries' => $queries)); } return $data; }
/** * Command handler * * @param string $name The command name * @param CommandContext $context The command context * * @return mixed Method result if the method exsist, NULL otherwise. */ public function execute($name, CommandContext $context) { $type = ''; $result = null; if ($context->getSubject()) { $identifier = clone $context->getSubject()->getIdentifier(); if ($identifier->path) { $type = array_shift($identifier->path); } else { $type = $identifier->name; } } $parts = explode('.', $name); $method = !empty($type) ? '_' . $type . ucfirst(StringInflector::implode($parts)) : '_' . lcfirst(StringInflector::implode($parts)); //If the method exists call the method and return the result if (in_array($method, $this->getMethods())) { $result = $this->{$method}($context); } return $result; }
/** * Parse the identifier * * @param ObjectIdentifier $identifier An object identifier * @return array */ public function parseIdentifier(ObjectIdentifier $identifier) { $info = parent::parseIdentifier($identifier); $path = $identifier->path; //Allow locating default classes if $path is empty. if (empty($path)) { $info['path'] = $info['file']; $info['file'] = ''; $info['package'] = ''; } else { $package = array_shift($path); $info['path'] = StringInflector::implode($path); $info['package'] = ucfirst($package); } //Make an exception for 'view' and 'module' types if (in_array($info['package'], array('View', 'Module')) && !in_array('behavior', $path)) { $info['path'] = ''; } return $info; }
/** * Command handler * * This functions returns void to prevent is from breaking the chain. * * @param string $name The command name * @param object $context The command context * @return void */ public function execute($name, CommandContext $context) { $type = ''; if ($context->getSubject()) { $identifier = clone $context->getSubject()->getIdentifier(); if ($identifier->path) { $type = array_shift($identifier->path); } else { $type = $identifier->name; } } $parts = explode('.', $name); $name = 'on' . ucfirst(array_shift($parts)) . ucfirst($type) . StringInflector::implode($parts); if ($this->getConfig()->clone_context) { $event = clone $context; } else { $event = $context; } $event = new Event($event); $event->setTarget($context->getSubject()); $this->getEventDispatcher()->dispatchEvent($name, $event); }
/** * Search the behaviors to see if this table behaves as. * * Function is also capable of checking is a behavior has been mixed successfully using is[Behavior] function. * If the behavior exists the function will return TRUE, otherwise FALSE. * * @param string $method The function name * @param array $arguments The function arguments * @throws \BadMethodCallException If method could not be found * @return mixed The result of the function */ public function __call($method, $arguments) { // If the method is of the form is[Bahavior] handle it. $parts = StringInflector::explode($method); if ($parts[0] == 'is' && isset($parts[1])) { if (!$this->hasBehavior(strtolower($parts[1]))) { return false; } } return parent::__call($method, $arguments); }
/** * Get a list of the computed properties * * @return array An array */ public function getComputedProperties() { if (!$this->__computed_properties) { $properties = array(); foreach ($this->getMethods() as $method) { if (substr($method, 0, 11) == 'getProperty' && $method !== 'getProperty') { $property = StringInflector::underscore(substr($method, 11)); $properties[$property] = $property; } } $this->__computed_properties = $properties; } return $this->__computed_properties; }
/** * Get an instance of a row object for this rowset * * @param array $options An optional associative array of configuration settings. * @return DatabaseRowInterface */ public function getRow(array $options = array()) { $identifier = clone $this->getIdentifier(); $identifier->path = array('database', 'row'); $identifier->name = StringInflector::singularize($this->getIdentifier()->name); //The row default options $options['identity_column'] = $this->getIdentityColumn(); return $this->getObject($identifier, $options); }
/** * Method to set a table object attached to the model * * @param mixed $table An object that implements ObjectInterface, ObjectIdentifier object * or valid identifier string * @throws \UnexpectedValueException If the identifier is not a table identifier * @return ModelDatabase */ public function setTable($table) { if (!$table instanceof DatabaseTableInterface) { if (is_string($table) && strpos($table, '.') === false) { $identifier = $this->getIdentifier()->toArray(); $identifier['path'] = array('database', 'table'); $identifier['name'] = StringInflector::pluralize(StringInflector::underscore($table)); $identifier = $this->getIdentifier($identifier); } else { $identifier = $this->getIdentifier($table); } if ($identifier->path[1] != 'table') { throw new \UnexpectedValueException('Identifier: ' . $identifier . ' is not a table identifier'); } $table = $identifier; } $this->_table = $table; return $this; }
/** * Search the mixin method map and call the method or forward the call to each row * * This function implements a just in time mixin strategy. Available table behaviors are only mixed when needed. * Lazy mixing is triggered by calling DatabaseRowTable::is[Behaviorable](); * * @param string $method The function name * @param array $arguments The function arguments * @return mixed The result of the function */ public function __call($method, $arguments) { if ($this->isConnected() && !isset($this->_mixed_methods[$method])) { $parts = StringInflector::explode($method); //Check if a behavior is mixed if ($parts[0] == 'is' && isset($parts[1])) { //Lazy mix behaviors $behavior = strtolower($parts[1]); if ($this->getTable()->hasBehavior($behavior)) { $this->mixin($this->getTable()->getBehavior($behavior)); } else { return false; } } } return parent::__call($method, $arguments); }
/** * Find the referrer based on the context * * Method is being called when no referrer can be found in the request or when request url and referrer are * identical. Function should return a url that is different from the request url to avoid redirect loops. * * @param ControllerContextModel $context * @return HttpUrl A HttpUrl object */ public function findReferrer(ControllerContextModel $context) { $controller = $this->getMixer(); $identifier = $controller->getIdentifier(); $component = $identifier->package; $view = StringInflector::pluralize($identifier->name); $referrer = $controller->getView()->getRoute('component=' . $component . '&view=' . $view, true, false); return $this->getObject('lib:http.url', array('url' => $referrer)); }
/** * Forward the call to the current row * * Search the mixin method map and call the method or forward the call to each row * * This function implements a just in time mixin strategy. Available table behaviors are only mixed when needed. * Lazy mixing is triggered by calling DatabaseRowTable::is[Behaviorable](); * * @param string $method The function name * @param array $arguments The function arguments * @throws \BadMethodCallException If method could not be found * @return mixed The result of the function */ public function __call($method, $arguments) { $result = null; if ($this->isConnected()) { $parts = StringInflector::explode($method); //Check if a behavior is mixed if ($parts[0] == 'is' && isset($parts[1])) { $row = $this->getIterator()->current(); if ($row && !in_array($method, $row->getMethods())) { //Lazy mix behaviors $behavior = strtolower($parts[1]); if ($row->getTable()->hasBehavior($behavior)) { $row->mixin($row->getTable()->getBehavior($behavior)); } else { return false; } } } } if ($row = $this->getIterator()->current()) { // Call_user_func_array is ~3 times slower than direct method calls. switch (count($arguments)) { case 0: $result = $row->{$method}(); break; case 1: $result = $row->{$method}($arguments[0]); break; case 2: $result = $row->{$method}($arguments[0], $arguments[1]); break; case 3: $result = $row->{$method}($arguments[0], $arguments[1], $arguments[2]); break; default: // Resort to using call_user_func_array for many segments $result = call_user_func_array(array($row, $method), $arguments); } } return $result; }
/** * Method to set a model object attached to the controller * * @param mixed $model An object that implements ObjectInterface, ObjectIdentifier object * or valid identifier string * @return ViewAbstract */ public function setModel($model) { if (!$model instanceof ModelInterface) { if (is_string($model) && strpos($model, '.') === false) { // Model names are always plural if (StringInflector::isSingular($model)) { $model = StringInflector::pluralize($model); } $identifier = clone $this->getIdentifier(); $identifier->path = array('model'); $identifier->name = $model; } else { $identifier = $this->getIdentifier($model); } $model = $identifier; } $this->_model = $model; return $this; }
/** * Load a template helper * * This function accepts a partial identifier, in the form of helper.function or schema:package.helper.function. If * a partial identifier is passed a full identifier will be created using the template identifier. * * If the view state have the same string keys, then the parameter value for that key will overwrite the state. * * @param string $identifier Name of the helper, dot separated including the helper function to call * @param array $params An optional associative array of functions parameters to be passed to the helper * @return string Helper output * @throws \BadMethodCallException If the helper function cannot be called. */ public function renderHelper($identifier, $params = array()) { //Get the function and helper based on the identifier $parts = explode('.', $identifier); $function = array_pop($parts); $identifier = array_pop($parts); //Handle schema:package.helper.function identifiers if (!empty($parts)) { $identifier = implode('.', $parts) . '.template.helper.' . $identifier; } $helper = $this->getHelper($identifier, $params); //Call the helper function if (!is_callable(array($helper, $function))) { throw new \BadMethodCallException(get_class($helper) . '::' . $function . ' not supported.'); } //Merge the view state with the helper params $view = $this->getView(); if (StringInflector::isPlural($view->getName())) { if ($state = $view->getModel()->getState()) { $params = array_merge($state->getValues(), $params); } } else { if ($item = $view->getModel()->getRow()) { $params = array_merge($item->toArray(), $params); } } return $helper->{$function}($params); }
/** * Command handler * * @param CommandInterface $command The command * @param CommandChainInterface $chain The chain executing the command * @return mixed|null If a handler breaks, returns the break condition. NULL otherwise. */ public function execute(CommandInterface $command, CommandChainInterface $chain) { $type = ''; $package = ''; $subject = ''; if ($command->getSubject()) { $identifier = $command->getSubject()->getIdentifier()->toArray(); $package = $identifier['package']; if ($identifier['path']) { $type = array_shift($identifier['path']); $subject = $identifier['name']; } else { $type = $identifier['name']; } } $parts = explode('.', $command->getName()); $when = array_shift($parts); // Before or After $name = StringInflector::implode($parts); // Action // Create Specific and Generic event names $event_specific = 'on' . ucfirst($when) . ucfirst($package) . ucfirst($subject) . ucfirst($type) . $name; $event_generic = 'on' . ucfirst($when) . ucfirst($type) . $name; // Clone the context if ($this->isEventImmutable()) { $event = clone $command; } else { $event = $command; } // Create event object to check for propagation $event = $this->getEventPublisher()->publishEvent($event_specific, $event->getAttributes(), $event->getSubject()); // Ensure event can be propagated and event name is different if ($event->canPropagate() && $event_specific != $event_generic) { $event->setName($event_generic); $this->getEventPublisher()->publishEvent($event); } }
/** * Method to set a controller object attached to the dispatcher * * @param mixed $controller An object that implements ControllerInterface, ObjectIdentifier object * or valid identifier string * @return DispatcherAbstract */ public function setController($controller, $config = array()) { if (!$controller instanceof ControllerInterface) { if (is_string($controller) && strpos($controller, '.') === false) { // Controller names are always singular if (StringInflector::isPlural($controller)) { $controller = StringInflector::singularize($controller); } $identifier = clone $this->getIdentifier(); $identifier->path = array('controller'); $identifier->name = $controller; } else { $identifier = $this->getIdentifier($controller); } //Set the configuration $identifier->setConfig($config); $controller = $identifier; } $this->_controller = $controller; return $this; }
/** * Drag and Drop Sortables Behavior * * @param array $config An optional array with configuration options * @return string Html */ public function sortable($config = array()) { $config = new ObjectConfigJson($config); $config->append(array('option' => 'com_' . $this->getIdentifier()->getPackage(), 'view' => StringInflector::singularize($this->getTemplate()->getView()->getName()), 'selector' => 'table tbody.sortable', 'direction' => 'asc', 'url' => '?format=json'))->append(array('options' => array('handle' => 'td.handle', 'numcolumn' => '.grid-count', 'direction' => $config->direction, 'adapter' => array('type' => 'koowa', 'options' => array('url' => $config->url, 'data' => array('_token' => $this->getObject('user')->getSession()->getToken(), '_action' => 'edit'), 'key' => 'order', 'offset' => 'relative'))))); $html = ''; $signature = md5(serialize(array($config->selector, $config->options))); if (!isset($this->_loaded[$signature])) { $options = !empty($config->options) ? $config->options->toArray() : array(); $html .= "\n <script src=\"/administrator/theme/default/js/sortables.js\" />\n <style src=\"/administrator/theme/default/stylesheets/sortables.css\" />\n\t\t\t\t<script>\n\t\t\t\t(function(){\n\t\t\t\t\tvar sortable = function() {\n\t\t\t\t\t\t\$\$('" . $config->selector . "').sortable(" . json_encode($options) . ");\n\t\t\t\t\t};\n\t\t\t\t\twindow.addEvents({domready: sortable, request: sortable});\n\t\t\t\t})();\n\t\t\t\t</script>\n\t\t\t"; $this->_loaded[$signature] = true; } return $html; }
/** * Supports a simple form of Fluent Interfaces. Allows you to assign variables to the view by using the variable * name as the method name. If the method name is a setter method the setter will be called instead. * * For example : $view->data(array('foo' => 'bar'))->title('name')->render() * * @param string $method Method name * @param array $args Array containing all the arguments for the original call * @return ViewAbstract * * @see http://martinfowler.com/bliki/FluentInterface.html */ public function __call($method, $args) { if (!$this->isMixedMethod($method)) { //If one argument is passed we assume a setter method is being called if (count($args) == 1) { if (!method_exists($this, 'set' . ucfirst($method))) { $this->{$method} = $args[0]; return $this; } else { return $this->{'set' . ucfirst($method)}($args[0]); } } //Check if a behavior is mixed $parts = StringInflector::explode($method); if ($parts[0] == 'is' && isset($parts[1])) { return false; } } return parent::__call($method, $args); }
/** * Search the mixin method map and call the method or trigger an error * * This function check to see if the method exists in the mixing map if not it will call the 'listbox' function. * The method name will become the 'name' in the config array. * * This can be used to auto-magically create select filters based on the function name. * * @param string $method The function name * @param array $arguments The function arguments * @throws \BadMethodCallException If method could not be found * @return mixed The result of the function */ public function __call($method, $arguments) { if (!in_array($method, $this->getMethods())) { $config = $arguments[0]; if (!isset($config['name'])) { $config['name'] = StringInflector::singularize(strtolower($method)); } return $this->_render($config); } return parent::__call($method, $arguments); }
/** * Fetch a new entity from the data store * * @param ModelContext $context A model context object * @return ModelEntityInterface The entity */ protected function _actionFetch(ModelContext $context) { $identifier = $this->getIdentifier()->toArray(); $identifier['path'] = array('model', 'entity'); $identifier['name'] = StringInflector::pluralize($identifier['name']); $options = array('identity_key' => $context->getIdentityKey()); return $this->getObject($identifier, $options); }
/** * Render action * * This function translates a render request into a read or browse action. If the view name is singular a read * action will be executed, if plural a browse action will be executed. * * @param CommandContext $context A command context object * @return string|false The rendered output of the view or FALSE if something went wrong */ protected function _actionRender(CommandContext $context) { //Check if we are reading or browsing $action = StringInflector::isSingular($this->getView()->getName()) ? 'read' : 'browse'; //Execute the action $this->execute($action, $context); return parent::_actionRender($context); }
/** * Add a command by it's name * * @param string Method name * @param array Array containing all the arguments for the original call * @return mixed * @see addCommand() */ public function __call($method, $args) { $parts = StringInflector::explode($method); if ($parts[0] == 'add' && isset($parts[1])) { $config = isset($args[0]) ? $args[0] : array(); $command = $this->addCommand(strtolower($parts[1]), $config); return $command; } return parent::__call($method, $args); }
/** * Execute a controller action by it's name. * * Function is also capable of checking is a behavior has been mixed successfully using is[Behavior] function. If * the behavior exists the function will return TRUE, otherwise FALSE. * * @param string $method Method name * @param array $args Array containing all the arguments for the original call * @return mixed * @see execute() */ public function __call($method, $args) { //Handle action alias method if (in_array($method, $this->getActions())) { //Get the data $data = !empty($args) ? $args[0] : array(); //Create a context object if (!$data instanceof CommandInterface) { $context = $this->getContext(); //Store the parameters in the context $context->param = $data; //Force the result to false before executing $context->result = false; } else { $context = $data; } //Execute the action return $this->execute($method, $context); } if (!$this->isMixedMethod($method)) { //Check if a behavior is mixed $parts = StringInflector::explode($method); if ($parts[0] == 'is' && isset($parts[1])) { return false; } } return parent::__call($method, $args); }