/** * Get the name of the id field. * @return string Name of id field. */ protected function getId() { $class = Utilities::getClassName($this); if (preg_match('/^(.+)MetaSchema$/', $class, $matches) !== 1) { throw new InvalidMixinException(tr('Invalid meta class name format.')); } return lcfirst($matches[1]) . 'Id'; }
/** * Touch a state document (make sure that it exists). * @param string $key State document key. * @return bool True if document exists. */ public function touch($key) { if (!isset($this->files[$key])) { if (!Utilities::dirExists($this->dir, true)) { return false; } $this->files[$key] = new PhpStore($this->dir . '/' . $key . '.php'); } return $this->files[$key]->touch(); }
/** * Get a JTK tool/snippet. * @param string $name Tool name. * @return JtkSnippet Snippet object for tool. */ public function getTool($name) { // if (isset($this->toolInstances[$name])) // return $this->toolInstances[$name]; if (isset($this->tools[$name])) { $class = $this->tools[$name]; Utilities::assumeSubclassOf($class, 'Jivoo\\Jtk\\JtkSnippet'); return new $class($this->app); } return null; }
/** * {@inheritdoc} */ public function __set($property, $value) { switch ($property) { case 'template': $this->type = Utilities::getContentType($value); case 'data': case 'withLayout': $this->{$property} = $value; return; } parent::__set($property, $value); }
/** * Constructor * @param string $name Name of schema */ public function __construct($name = null) { $className = get_class($this); if ($className != __CLASS__) { if (!isset($name)) { $name = preg_replace('/Schema$/', '', Utilities::getClassName($className)); } if (defined($className . '::REVISION')) { $this->_revision = constant($className . '::REVISION'); } $this->createSchema(); $this->_readOnly = true; } if (isset($name)) { $this->_name = $name; } }
/** * Construct file log handler. * @param string $filePath Log file path. * @param string $level Minimum log level, see {@see \Psr\Log\LogLevel}. * @param bool $useLocking Whether to lock the file before appending to ensure * atomicity of each write. */ public function __construct($filePath, $level = LogLevel::DEBUG, $useLocking = false) { if (!file_exists($filePath)) { $dir = dirname($filePath); if (!Utilities::dirExists($dir)) { trigger_error(tr('Could not create log directory: %1', $dir), E_USER_WARNING); $this->stream = false; return; } if (!touch($filePath)) { trigger_error(tr('Could not create log file: %1', $filePath), E_USER_WARNING); $this->stream = false; return; } } $this->file = realpath($filePath); parent::__construct(null, $level, $useLocking); }
/** * Get values of an enum class. * @param string $class Class name. * @throws InvalidEnumException If the class invalid or does not contain * constants. * @return string[] Enum values. */ public static function getValues($class = null) { if (!isset($class)) { $class = get_called_class(); } if (!isset(self::$values[$class])) { if (!self::classExists($class)) { throw new InvalidEnumException(tr('Enum class not found: %1', $class)); } $class = self::$classes[$class]; Utilities::assumeSubclassOf($class, 'Jivoo\\Models\\Enum'); $ref = new \ReflectionClass($class); self::$values[$class] = array_flip($ref->getConstants()); if (count(self::$values[$class]) < 1) { throw new InvalidEnumException(tr('Enum type "%1" must contain at least one constant', $class)); } } return self::$values[$class]; }
/** * {@inheritdoc} */ public function __invoke($parameters = array()) { $object = null; if (isset($parameters['object'])) { $object = $parameters['object']; } else { if (isset($parameters[0])) { $object = $parameters[0]; } } if (isset($object)) { Utilities::assumeSubclassOf($object, $this->objectType); } else { throw new InvalidArgumentException(tr('JTK object is null')); } $this->viewData['object'] = $object; $response = parent::__invoke($parameters); if ($response instanceof ViewResponse) { return $response->body; } return $response; }
/** * Find a resource. * @param string $resource Resource name. * @throws ResourceTypeException If unknown type of resource. * @return array Description of resource and dependencies. */ private function find($resource) { if (isset($this->providers[$resource])) { return $this->providers[$resource]; } $type = Utilities::getFileExtension($resource); switch ($type) { case 'js': $type = 'script'; $location = $this->assets->getAsset('js/' . $resource); break; case 'css': $type = 'style'; $location = $this->assets->getAsset('css/' . $resource); break; default: throw new ResourceTypeException(tr('Unknown type of resource: "%1"', $type)); } if (!isset($location)) { $location = 'resource-is-missing/' . $resource; } return array('location' => $location, 'type' => $type, 'dependencies' => array(), 'condition' => null); }
/** * Run a migration on a database. Will attempt to revert if migration fails. * @param string $dbName Name of database. * @param string $migrationName Name of migration. * @throws MigrationException If migration fails. */ public function run($dbName, $migrationName) { $db = $this->getDatabase($dbName); $this->logger->info('Initializing migration ' . $migrationName); Utilities::assumeSubclassOf($migrationName, 'Jivoo\\Migrations\\Migration'); // The migration schema keeps track of the state of the database if (!isset($this->migrationSchemas[$dbName])) { $this->migrationSchemas[$dbName] = new MigrationSchema($db); } $migrationSchema = $this->migrationSchemas[$dbName]; $migration = new $migrationName($db, $migrationSchema); try { $migration->up(); $db->SchemaRevision->insert(array('revision' => $migrationName)); } catch (\Exception $e) { $migration->revert(); throw new MigrationException(tr('Migration failed: ' . $migrationName), null, $e); } }
/** * Set value of property. * @param string $name Property name. * @param string $value Value of property. * @throws InvalidPropertyException If unknown property. */ public function __set($property, $value) { switch ($property) { case 'status': case 'modified': case 'maxAge': $this->{$property} = $value; return; case 'type': $this->type = Utilities::convertType($value); return; } throw new InvalidPropertyException(tr('Invalid property: %1', $property)); }
/** * Get a module provided by an extension. Load it if has not yet been * loaded. * @param string $name Name of module. * @return ExtensionModule Module. * @throws InvalidExtensionException If extension module not found in the * load list, i.e. if the extension that provides the module has not been * imported. */ public function getModule($name) { if (!isset($this->extensionModules[$name])) { if (!isset($this->loadList[$name])) { throw new InvalidExtensionException(tr('Extension not in load list: "%1"', $name)); } $this->triggerEvent('beforeLoadExtension', new LoadExtensionEvent($this, $name)); Utilities::assumeSubclassOf($name, 'Jivoo\\Extensions\\ExtensionModule'); $info = $this->loadList[$name]; $this->extensionModules[$name] = new $name($this->app, $info, $this->config['config'][$info->canonicalName]); $this->triggerEvent('afterLoadExtension', new LoadExtensionEvent($this, $name, $this->extensionModules[$name])); } return $this->extensionModules[$name]; }
/** * Whether or not the client accepts the specified type. If the type is * omitted then a list of acceptable types is returned. * @param string $type Type, can be a MIME type or a file extension known by * {@see Utilities::convertType()}. * @return bool|string[] True if client accepts provided type, false otherwise. * List of accepted MIME types if type parameter omitted. */ public function accepts($type = null) { if (!isset($type)) { return $this->accepts; } return in_array(Utilities::convertType($type), $this->accepts); }
/** * Construct active model. * @param App $app Associated application. * @param DatabaseLoader $databases Databases module. * @throws InvalidActiveModelException If model is incorrectly defined. * @throws InvalidTableException If table not found. * @throws InvalidAssociationException If association models are invalid. * @throws InvalidMixinException If a mixin is invalid. */ public final function __construct(App $app, Loader $databases) { parent::__construct($app); $databaseName = $this->database; $database = $databases->{$databaseName}; $this->name = Utilities::getClassName(get_class($this)); if (!isset($database)) { throw new InvalidActiveModelException(tr('Database "%1" not found', $this->database)); } $this->database = $database; if (!isset($this->table)) { $this->table = $this->name; } $table = $this->table; if (!isset($this->database->{$table})) { throw new InvalidTableException(tr('Table "%1" not found in database', $table)); } $this->source = $this->database->{$table}; $this->schema = $this->source->getSchema(); if (!isset($this->schema)) { throw new InvalidTableException('Schema for table "' . $table . '" not found'); } $pk = $this->schema->getPrimaryKey(); if (count($pk) == 1) { $pk = $pk[0]; $this->primaryKey = $pk; $type = $this->schema->{$pk}; if ($type->isInteger() and $type->autoIncrement) { $this->aiPrimaryKey = $pk; } } else { throw new InvalidActiveModelException(tr('ActiveModel does not support multi-field primary keys')); } $this->nonVirtualFields = $this->schema->getFields(); $this->fields = $this->nonVirtualFields; foreach ($this->virtual as $field) { $this->fields[] = $field; $this->virtualFields[] = $field; } $this->validator = new ValidatorBuilder($this, $this->validate); $this->schema->createValidationRules($this->validator); foreach ($this->nonVirtualFields as $field) { $type = $this->schema->{$field}; if (isset($type->default)) { $this->defaults[$field] = $type->default; } } if (isset($this->record)) { Utilities::assumeSubclassOf($this->record, 'Jivoo\\ActiveModels\\ActiveRecord'); } $this->attachEventListener($this); foreach ($this->mixins as $mixin => $options) { if (!is_string($mixin)) { $mixin = $options; $options = array(); } if (class_exists('Jivoo\\ActiveModels\\' . $mixin . 'Mixin')) { $mixin = 'Jivoo\\ActiveModels\\' . $mixin . 'Mixin'; } else { if (class_exists($mixin . 'Mixin')) { $mixin .= 'Mixin'; } else { if (!class_exists($mixin)) { throw new InvalidMixinException('Mixin class not found: ' . $mixin); } } } Assume::isSubclassOf($mixin, 'Jivoo\\ActiveModels\\ActiveModelMixin'); $mixin = new $mixin($this->app, $this, $options); $this->attachEventListener($mixin); $this->mixinObjects[] = $mixin; foreach ($mixin->getMethods() as $method) { $this->methods[$method] = array($mixin, $method); } } $this->database->{$table} = $this; $this->init(); }
/** * {@inheritdoc} */ public function before() { $this->view->data->title = tr('I18n'); if (isset($this->m->Extensions)) { $this->viewData['extensions'] = $this->m->Extensions->listAllExtensions(); } else { $this->viewData['extensions'] = null; } if (isset($this->m->Themes)) { $this->viewData['themes'] = $this->m->Themes->listAllThemes(); } else { $this->viewData['themes'] = null; } $this->viewData['dir'] = $this->p('app/languages'); $this->viewData['project'] = $this->app->name . ' ' . $this->app->version; $this->rootDir = $this->p('app'); if (isset($this->request->query['scope'])) { $scope = $this->request->query['scope']; if ($scope === 'lib') { $this->scope = 'lib'; $this->viewData['dir'] = $this->p('Core/languages'); $this->viewData['project'] = 'Jivoo ' . \Jivoo\VERSION; $this->rootDir = \Jivoo\PATH; } else { if (strpos($scope, '-') !== false) { $scope = explode('-', $scope); if (isset($this->m->Extensions) and $scope[0] === 'extension') { if (isset($this->viewData['extensions'][$scope[1]])) { $this->scope = 'extension'; $this->extension = $this->viewData['extensions'][$scope[1]]; $this->viewData['dir'] = $this->extension->p($this->app, 'languages'); $this->viewData['project'] = $this->extension->name . ' ' . $this->extension->version; $this->rootDir = $this->extension->p($this->app, ''); } } else { if (isset($this->m->Themes) and $scope[0] === 'theme') { if (isset($this->viewData['themes'][$scope[1]])) { $this->scope = 'theme'; $this->theme = $this->viewData['themes'][$scope[1]]; $this->viewData['dir'] = $this->theme->p($this->app, 'languages'); $this->viewData['project'] = $this->theme->name . ' ' . $this->theme->version; $this->rootDir = $this->theme->p($this->app, ''); } } } } } } $this->viewData['dirExists'] = Utilities::dirExists($this->viewData['dir']); if ($this->viewData['dirExists']) { $this->viewData['languages'] = $this->findLanguages($this->viewData['dir']); } return parent::before(); }
/** * Insert one or more objects. * @param int $offset The offset to insert the objects(s) at. * @param JtkObject|JtkObject[] $item Object or array of objects with * optional keys. * @param string $id Optional id for object. */ public function insert($offset, $item, $id = null) { if (is_array($item)) { $item = array_reverse($item, true); foreach ($item as $id => $it) { if (!is_string($id)) { $id = null; } $this->insert($offset, $it, $id); } } else { Utilities::assumeSubclassOf($item, $this->type); if (isset($id)) { $itemArray = array($id => $item); } else { $itemArray = array($item); } $head = array_splice($this->items, 0, $offset); $this->items = array_merge($head, $itemArray, $this->items); } }
/** * Get multiple helpers. * @param string[] $names Names of helpers. * @return Helper[] Helper objects. */ public function getHelpers($names) { $helpers = array(); foreach ($names as $name) { $helper = $this->getHelper($name); if (strpos($name, '\\') !== false) { $name = Utilities::getClassName($name); } $helpers[$name] = $helper; } return $helpers; }
/** * Convert table name. E.g. "UserSession" to "prefix_user_session". * @param string $name Table name. * @return string Real table name. */ public function tableName($name) { return $this->tablePrefix . Utilities::camelCaseToUnderscores($name); }
/** * {@inheritdoc} */ public function autoRoute(RoutingTable $table, $route, $resource = false) { $controller = $route['controller']; $dirs = explode('\\', $controller); if ($dirs == array('App')) { $dirs = array(); } else { $dirs = array_map(array('Jivoo\\Core\\Utilities', 'camelCaseToDashes'), $dirs); } $patternBase = implode('/', $dirs); if ($resource) { $table->match($patternBase, 'action:' . $controller . '::index'); $table->match($patternBase . '/add', 'action:' . $controller . '::add'); //C $table->match($patternBase . '/:0', 'action:' . $controller . '::view'); //R $table->match($patternBase . '/:0/edit', 'action:' . $controller . '::edit'); //U $table->match($patternBase . '/:0/delete', 'action:' . $controller . '::delete'); //D $table->match('DELETE ' . $patternBase . '/:0', 'action:' . $controller . '::delete'); $table->match('PATCH ' . $patternBase . '/:0', 'action:' . $controller . '::edit'); $table->match('PUT ' . $patternBase . '/:0', 'action:' . $controller . '::edit'); $table->match('POST ' . $patternBase, 'action:' . $controller . '::add'); return $patternBase . '/:0'; } else { if (isset($route['action'])) { $action = $route['action']; $class = $this->getClass($controller); if (!$class) { throw new InvalidClassException(tr('Invalid controller: %1', $controller)); } $route = array('controller' => $controller, 'action' => $action); $reflect = new \ReflectionMethod($class, $action); $required = $reflect->getNumberOfRequiredParameters(); $total = $reflect->getNumberOfParameters(); if (!empty($prefix) and substr($prefix, -1) != '/') { $prefix .= '/'; } if ($action == 'index') { $table->match($patternBase, $route); } $patternBase .= '/' . str_replace('_', '.', Utilities::camelCaseToDashes($action)); if ($required < 1) { $table->match($patternBase, $route); } $path = $patternBase; for ($i = 0; $i < $total; $i++) { $path .= '/*'; if ($i <= $required) { $table->match($path, $route); } } return $patternBase; } else { $actions = $this->getActions($controller); if ($actions === false) { throw new InvalidClassException(tr('Invalid controller: %1', $controller)); } foreach ($actions as $action) { $route['action'] = $action; $this->autoRoute($table, $route, false); } return $patternBase; } } }
/** * {@inheritdoc} */ public function getTables() { $prefix = $this->db->tableName(''); $prefixLength = strlen($prefix); $result = $this->db->rawQuery('SELECT name FROM sqlite_master WHERE type = "table"'); $tables = array(); while ($row = $result->fetchRow()) { $name = $row[0]; if (substr($name, 0, $prefixLength) == $prefix) { $name = substr($name, $prefixLength); $tables[] = Utilities::underscoresToCamelCase($name); } } return $tables; }
/** * Render a template. * * If $templateName is not set, the path of the template will be computed * based on the name of the controller and the name of the action. * * @param string $templateName Name of template to render. * @return ViewResponse A view response for template. */ protected function render($templateName = null) { if (!$this->response instanceof ViewResponse) { $response = $this->response; $this->response = new ViewResponse(Http::OK, $this->view); return $response; } if (!isset($templateName)) { list(, $caller) = debug_backtrace(false); $class = str_replace($this->app->n('Controllers\\'), '', $caller['class']); $class = preg_replace('/Controller$/', '', $class); $templateName = ''; if ($class != 'App') { $dirs = array_map(array('Jivoo\\Core\\Utilities', 'camelCaseToDashes'), explode('\\', $class)); $templateName = implode('/', $dirs) . '/'; } $type = 'html'; $action = $caller['function']; if (strpos($action, '_') !== false and preg_match('/^(.*)_([a-z0-9]+)$/i', $action, $matches) === 1) { $action = $matches[1]; $type = $matches[2]; } $templateName .= Utilities::camelCaseToDashes($action) . '.' . $type; } $this->response->template = $templateName; $response = $this->response; $this->response = new ViewResponse(Http::OK, $this->view); return $response; }
/** * Find layout template for a template. * @param string $template Template name. * @return string|null Name of layout template or null if not found. */ public function findLayout($template) { if (Utilities::isAbsolutePath($template)) { return null; } $extension = Utilities::getFileExtension($template); $dir = $template; do { $dir = dirname($dir); if ($dir === '.') { $template = 'layout.' . $extension; } else { $template = $dir . '/layout.' . $extension; } $file = $this->findTemplate($template); if (isset($file)) { return $template; } } while ($dir != '.'); return null; }
/** * {@inheritdoc} */ public function getTables() { $prefix = $this->db->tableName(''); $prefixLength = strlen($prefix); $result = $this->db->rawQuery("SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public'"); $tables = array(); while ($row = $result->fetchRow()) { $name = $row[0]; if (substr($name, 0, $prefixLength) == $prefix) { $name = substr($name, $prefixLength); $tables[] = Utilities::underscoresToCamelCase($name); } } return $tables; }
/** * {@inheritdoc} */ protected function render($templateName = null) { if (!isset($templateName)) { list(, $caller) = debug_backtrace(false); $class = str_replace($this->app->n('Snippets\\'), '', $caller['class']); $dirs = array_map(array('Jivoo\\Core\\Utilities', 'camelCaseToDashes'), explode('\\', $class)); $templateName = implode('/', $dirs) . '/'; $templateName .= Utilities::camelCaseToDashes($caller['function']) . '.html'; } return parent::render($templateName); }
/** * Insert an image. * @param $file Path to file (can be an asset or an absolute path). * @param string[] $attributes Attributes, see {@see Html::readAttributes}. * @return string HTML image. */ public function img($file, $attributes = array()) { $img = $this->create('img'); $img->attr('alt', $file); $img->attr($attributes); if (!Utilities::isAbsolutePath($file)) { $file = $this->view->file($file); } $img->attr('src', $file); return $img->toString(); }
/** * Add an ACL module. * @param Acl $acl Module. * @param string $name Name that can be used to later access the module using * {@see __get}, default is the class name (without namespace). */ public function addAcl(Acl $acl, $name = null) { $this->aclMethods[] = $acl; if (!isset($name)) { $name = Utilities::getClassName($acl); } $this->acModules[$name] = $acl; }
/** * Get link to a dynamic asset. * @param array|Linkable|string|null $route A route, see {@see \Jivoo\Routing\Routing}. * @return string|null Link to asset, or null if not found. */ public function getDynamicAsset($route) { $route = $this->m->Routing->validateRoute($route); if ($route['dispatcher'] instanceof ActionDispatcher) { $path = array_map(array('Jivoo\\Core\\Utilities', 'camelCaseToDashes'), explode('\\', $route['controller'])); $path[] = str_replace('_', '.', Utilities::camelCaseToDashes($route['action'])); } else { if ($route['dispatcher'] instanceof SnippetDispatcher) { $path = explode('\\', str_replace('_', '.', $route['snippet'])); $path = array_map(array('Jivoo\\Core\\Utilities', 'camelCaseToDashes'), $path); } else { return null; } } $suffix = ''; // if ($this->config['mtimeSuffix']) // $suffix = '?' . filemtime($file); return $this->m->Routing->getLinkFromPath(array_merge(array('assets'), $path), $route['query'], $route['fragment']) . $suffix; }
/** * Declaration getter. * @param string $property Property in camelCase, e.g. backgroundColor, * fontFamily, etc. * @return string|array Value. */ public function __get($property) { $property = Utilities::camelCaseToDashes($property); if (isset($this->declarations[$property])) { return $this->declarations[$property]; } return null; }
/** * Run build script. * @param string $root Package root. * @throws InstallException on failure. */ public function run($root) { $this->buildPath = $this->p('tmp/build/' . $this->name); if (!Utilities::dirExists($this->buildPath, true, true)) { throw new InstallException('Could not create build directory: ' . $this->buildPath); } $this->installPath = Paths::combinePaths($root, $this->name); if (!Utilities::dirExists($this->installPath, true, true)) { throw new InstallException('Could not create install directory: ' . $this->installPath); } if (isset($this->prepare)) { $this->info(tr('Preparing...')); call_user_func($this->prepare, $this); } $this->fetchSources(); if (isset($this->build)) { $this->info(tr('Building...')); call_user_func($this->build, $this); } if (isset($this->install)) { $this->info(tr('Installing...')); call_user_func($this->install, $this); } $this->manifest['name'] = $this->name; $this->manifest['version'] = $this->version; if (isset($this->manifestFile) and isset($this->manifest)) { $this->info(tr('Creating manifest...')); $manifestFile = $this->installPath . '/' . $this->manifestFile; file_put_contents($manifestFile, Json::prettyPrint($this->manifest)); } }
/** * Construct asset response. * @param string $file Path to asset. */ public function __construct($file) { parent::__construct(Http::OK, Utilities::getContentType($file)); $this->file = $file; $this->modified = filemtime($file); }