/** * Initializes the console app by creating the command runner. * * @return null */ public function init() { // Set default timezone to UTC date_default_timezone_set('UTC'); // Import all the built-in components foreach ($this->componentAliases as $alias) { Craft::import($alias); } // Attach our Craft app behavior. $this->attachBehavior('AppBehavior', new AppBehavior()); // Initialize Cache and LogRouter right away (order is important) $this->getComponent('cache'); $this->getComponent('log'); // So we can try to translate Yii framework strings $this->coreMessages->attachEventHandler('onMissingTranslation', array('Craft\\LocalizationHelper', 'findMissingTranslation')); // Set our own custom runtime path. $this->setRuntimePath(craft()->path->getRuntimePath()); // Attach our own custom Logger Craft::setLogger(new Logger()); // No need for these. craft()->log->removeRoute('WebLogRoute'); craft()->log->removeRoute('ProfileLogRoute'); // Load the plugins craft()->plugins->loadPlugins(); // Validate some basics on the database configuration file. craft()->validateDbConfigFile(); // Call parent::init before the plugin console command logic so craft()->commandRunner will be available to us. parent::init(); foreach (craft()->plugins->getPlugins() as $plugin) { $commandsPath = craft()->path->getPluginsPath() . StringHelper::toLowerCase($plugin->getClassHandle()) . '/consolecommands/'; if (IOHelper::folderExists($commandsPath)) { craft()->commandRunner->addCommands(rtrim($commandsPath, '/')); } } }
/** * Make handle from source name. * * @param $name * * @return string */ private function _makeHandle($name, $sourceId) { // Remove HTML tags $handle = preg_replace('/<(.*?)>/', '', $name); $handle = preg_replace('/<[\'"‘’“”\\[\\]\\(\\)\\{\\}:]>/', '', $handle); $handle = StringHelper::toLowerCase($handle); $handle = StringHelper::asciiString($handle); $handle = preg_replace('/^[^a-z]+/', '', $handle); // In case it was an all non-ASCII handle, have a default. if (!$handle) { $handle = 'source' . $sourceId; } $handleParts = preg_split('/[^a-z0-9]+/', $handle); $handle = ''; foreach ($handleParts as $index => &$part) { if ($index) { $part = ucfirst($part); } $handle .= $part; } $appendix = ''; while (true) { $taken = craft()->db->createCommand()->select('handle')->from('assetsources')->where('handle = :handle', array(':handle' => $handle . $appendix))->queryScalar(); if ($taken) { $appendix = (int) $appendix + 1; } else { break; } } return $handle . $appendix; }
public function counter($requestData) { $period = isset($requestData['period']) ? $requestData['period'] : null; $dimension = isset($requestData['options']['dimension']) ? $requestData['options']['dimension'] : null; $metric = isset($requestData['options']['metric']) ? $requestData['options']['metric'] : null; $start = date('Y-m-d', strtotime('-1 ' . $period)); $end = date('Y-m-d'); // Counter $criteria = new Analytics_RequestCriteriaModel(); $criteria->startDate = $start; $criteria->endDate = $end; $criteria->metrics = $metric; if ($dimension) { $optParams = array('filters' => $dimension . '!=(not set);' . $dimension . '!=(not provided)'); $criteria->optParams = $optParams; } $response = craft()->analytics->sendRequest($criteria); if (!empty($response['rows'][0][0]['f'])) { $count = $response['rows'][0][0]['f']; } else { $count = 0; } $counter = array('count' => $count, 'label' => StringHelper::toLowerCase(Craft::t(craft()->analytics_metadata->getDimMet($metric)))); // Return JSON return ['type' => 'counter', 'counter' => $counter, 'response' => $response, 'metric' => Craft::t(craft()->analytics_metadata->getDimMet($metric)), 'period' => $period, 'periodLabel' => Craft::t('this ' . $period)]; }
/** * Saves log messages in files. * * @param array $logs The list of log messages * * @return null */ protected function processLogs($logs) { $types = array(); foreach ($logs as $log) { $message = LoggingHelper::redact($log[0]); $level = $log[1]; $category = $log[2]; $time = $log[3]; $force = isset($log[4]) && $log[4] == true ? true : false; $plugin = isset($log[5]) ? StringHelper::toLowerCase($log[5]) : 'craft'; if (isset($types[$plugin])) { $types[$plugin] .= $this->formatLogMessageWithForce($message, $level, $category, $time, $force, $plugin); } else { $types[$plugin] = $this->formatLogMessageWithForce($message, $level, $category, $time, $force, $plugin); } } foreach ($types as $plugin => $text) { $text .= PHP_EOL . '******************************************************************************************************' . PHP_EOL; $this->setLogFile($plugin . '.log'); $logFile = IOHelper::normalizePathSeparators($this->getLogPath() . '/' . $this->getLogFile()); // Check the config setting first. Is it set to true? if (craft()->config->get('useWriteFileLock') === true) { $lock = true; } else { if (craft()->config->get('useWriteFileLock') === false) { $lock = false; } else { if (craft()->cache->get('useWriteFileLock') === 'yes') { $lock = true; } else { $lock = false; } } } $fp = @fopen($logFile, 'a'); if ($lock) { @flock($fp, LOCK_EX); } if (IOHelper::getFileSize($logFile) > $this->getMaxFileSize() * 1024) { $this->rotateFiles(); if ($lock) { @flock($fp, LOCK_UN); } @fclose($fp); if ($lock) { IOHelper::writeToFile($logFile, $text, false, true, false); } else { IOHelper::writeToFile($logFile, $text, false, true, true); } } else { @fwrite($fp, $text); if ($lock) { @flock($fp, LOCK_UN); } @fclose($fp); } } }
/** * Checks to see if the given name is valid in the enum. * * @param $name The name to search for. * @param bool $strict Defaults to false. If set to true, will do a case sensitive search for the name. * * @return bool true if it is a valid name, false otherwise. */ public static function isValidName($name, $strict = false) { $constants = static::_getConstants(); if ($strict) { return array_key_exists($name, $constants); } $keys = array_map(array('Craft\\StringHelper', 'toLowerCase'), array_keys($constants)); return in_array(StringHelper::toLowerCase($name), $keys); }
/** * A wrapper for writing to the log files for plugins that will ultimately call {@link Craft::log()}. This allows * plugins to be able to write to their own log files at `craft/storage/runtime/logs/pluginHandle.log` using * `PluginHandle::log()` syntax. * * @param $msg The message to be logged. * @param string $level The level of the message (e.g. LogLevel::Trace', LogLevel::Info, LogLevel::Warning or * LogLevel::Error). * @param bool $force Whether to force the message to be logged regardless of the level or category. * * @return mixed */ public static function log($msg, $level = LogLevel::Info, $force = false) { $plugin = get_called_class(); // Chunk off any namespaces $parts = explode('\\', $plugin); if (count($parts) > 0) { $plugin = $parts[count($parts) - 1]; } // Remove the trailing 'Plugin'. $plugin = str_replace('Plugin', '', $plugin); Craft::log($msg, $level, $force, 'plugin', StringHelper::toLowerCase($plugin)); }
/** * @param string $name * * @return mixed */ public function __get($name) { $plugin = craft()->plugins->getPlugin($name); if ($plugin && $plugin->isEnabled) { $pluginName = $plugin->getClassHandle(); $className = __NAMESPACE__ . '\\' . $pluginName . 'Variable'; // Variables should already be imported by the plugin service, but let's double check. if (!class_exists($className)) { Craft::import('plugins.' . StringHelper::toLowerCase($pluginName) . '.variables.' . $pluginName . 'Variable'); } return new $className(); } }
/** * Removes a route from the LogRouter by class name. * * @param $class * * @return null */ public function removeRoute($class) { $match = false; for ($counter = 0; $counter < sizeof($this->_routes); $counter++) { if (StringHelper::toLowerCase(get_class($this->_routes[$counter])) == StringHelper::toLowerCase(__NAMESPACE__ . '\\' . $class)) { $match = $counter; break; } } if (is_numeric($match)) { array_splice($this->_routes, $match, 1); } }
/** * Get the sections of the CP. * * @return array */ public function nav() { $nav['dashboard'] = array('label' => Craft::t('Dashboard')); if (craft()->sections->getTotalEditableSections()) { $nav['entries'] = array('label' => Craft::t('Entries')); } $globals = craft()->globals->getEditableSets(); if ($globals) { $nav['globals'] = array('label' => Craft::t('Globals'), 'url' => 'globals/' . $globals[0]->handle); } if (craft()->categories->getEditableGroupIds()) { $nav['categories'] = array('label' => Craft::t('Categories')); } if (craft()->assetSources->getTotalViewableSources()) { $nav['assets'] = array('label' => Craft::t('Assets')); } if (craft()->getEdition() == Craft::Pro && craft()->userSession->checkPermission('editUsers')) { $nav['users'] = array('label' => Craft::t('Users')); } // Add any Plugin nav items $plugins = craft()->plugins->getPlugins(); foreach ($plugins as $plugin) { if ($plugin->hasCpSection()) { if (craft()->userSession->checkPermission('accessPlugin-' . $plugin->getClassHandle())) { $lcHandle = StringHelper::toLowerCase($plugin->getClassHandle()); $nav[$lcHandle] = array('label' => $plugin->getName()); } } } // Allow plugins to modify the nav craft()->plugins->call('modifyCpNav', array(&$nav)); // Figure out which item is selected, and normalize the items $firstSegment = craft()->request->getSegment(1); if ($firstSegment == 'myaccount') { $firstSegment = 'users'; } foreach ($nav as $handle => &$item) { if (is_string($item)) { $item = array('label' => $item); } $item['sel'] = $handle == $firstSegment; if (isset($item['url'])) { $item['url'] = UrlHelper::getUrl($item['url']); } else { $item['url'] = UrlHelper::getUrl($handle); } } return $nav; }
/** * Creates a slug based on a given string. * * @param string $str * * @return string */ public static function createSlug($str) { // Remove HTML tags $slug = preg_replace('/<(.*?)>/u', '', $str); // Remove inner-word punctuation. $slug = preg_replace('/[\'"‘’“”\\[\\]\\(\\)\\{\\}:]/u', '', $slug); if (craft()->config->get('allowUppercaseInSlug') === false) { // Make it lowercase $slug = StringHelper::toLowerCase($slug); } // Get the "words". Split on anything that is not alphanumeric, or a period, underscore, or hyphen. preg_match_all('/[\\p{L}\\p{N}\\._-]+/u', $slug, $words); $words = ArrayHelper::filterEmptyStringsFromArray($words[0]); $slug = implode(craft()->config->get('slugWordSeparator'), $words); return $slug; }
/** * @param string $name * * @return mixed */ public function __get($name) { $plugin = craft()->plugins->getPlugin($name); if ($plugin && $plugin->isEnabled) { $pluginName = $plugin->getClassHandle(); $className = __NAMESPACE__ . '\\' . $pluginName . 'Variable'; // Variables should already be imported by the plugin service, but let's double check. if (!class_exists($className)) { Craft::import('plugins.' . StringHelper::toLowerCase($pluginName) . '.variables.' . $pluginName . 'Variable'); } // If we haven't done this one yet, create it and save it for later. if (!isset($this->_pluginVariableInstances[$className])) { $this->_pluginVariableInstances[$className] = new $className(); } return $this->_pluginVariableInstances[$className]; } }
/** * Backup All Forms */ public function backupAllForms() { $forms = FormBuilder2_FormRecord::model()->ordered()->findAll(); $table = 'craft_formbuilder2_forms'; if ($forms) { $this->_currentVersion = 'v' . craft()->getVersion() . '.' . craft()->getBuild(); $siteName = IOHelper::cleanFilename(StringHelper::asciiString(craft()->getSiteName())); $fileName = ($siteName ? $siteName . '_' : '') . gmdate('ymd_His') . '_' . $this->_currentVersion . '.sql'; $this->_filePath = craft()->path->getDbBackupPath() . StringHelper::toLowerCase($fileName); $this->_processHeader(); $results = $this->_processResult($table); $this->_processConstraints(); $this->_processFooter(); $filepath = $this->_filePath; $this->_processFile($fileName, $filepath); } else { return false; } }
/** * Triggers the database backup including all DML and DDL and writes it out to a file. * * @return string The path to the database backup file. */ public function run() { // Normalize the ignored table names if there is a table prefix set. if (($tablePrefix = craft()->config->get('tablePrefix', ConfigFile::Db)) !== '') { foreach ($this->_ignoreDataTables as $key => $tableName) { $this->_ignoreDataTables[$key] = $tablePrefix . '_' . $tableName; } } $this->_currentVersion = 'v' . craft()->getVersion() . '.' . craft()->getBuild(); $fileName = IOHelper::cleanFilename(craft()->getSiteName()) . '_' . gmdate('ymd_His') . '_' . $this->_currentVersion . '.sql'; $this->_filePath = craft()->path->getDbBackupPath() . StringHelper::toLowerCase($fileName); $this->_processHeader(); foreach (craft()->db->getSchema()->getTables() as $resultName => $val) { $this->_processResult($resultName); } $this->_processConstraints(); $this->_processFooter(); return $this->_filePath; }
/** * @param $object * @param $attribute * * @return null */ protected function validateAttribute($object, $attribute) { $handle = $object->{$attribute}; // Handles are always required, so if it's blank, the required validator will catch this. if ($handle) { $reservedWords = array_merge($this->reservedWords, static::$baseReservedWords); $reservedWords = array_map(array('Craft\\StringHelper', 'toLowerCase'), $reservedWords); $lcHandle = StringHelper::toLowerCase($handle); if (in_array($lcHandle, $reservedWords)) { $message = Craft::t('“{handle}” is a reserved word.', array('handle' => $handle)); $this->addError($object, $attribute, $message); } else { if (!preg_match('/^' . static::$handlePattern . '$/', $handle)) { $altMessage = Craft::t('“{handle}” isn’t a valid handle.', array('handle' => $handle)); $message = $this->message !== null ? $this->message : $altMessage; $this->addError($object, $attribute, $message); } } } }
/** * @param string $name command name (case-insensitive) * * @return \CConsoleCommand The command object. Null if the name is invalid. */ public function createCommand($name) { $name = StringHelper::toLowerCase($name); $command = null; if (isset($this->commands[$name])) { $command = $this->commands[$name]; } else { $commands = array_change_key_case($this->commands); if (isset($commands[$name])) { $command = $commands[$name]; } } if ($command !== null) { if (is_string($command)) { if (strpos($command, '/') !== false || strpos($command, '\\') !== false) { $className = IOHelper::getFileName($command, false); // If it's a default framework command, don't namespace it. if (strpos($command, 'framework') === false) { $className = __NAMESPACE__ . '\\' . $className; } if (!class_exists($className, false)) { require_once $command; } } else { $className = Craft::import($command); } return new $className($name, $this); } else { return Craft::createComponent($command, $name, $this); } } else { if ($name === 'help') { return new \CHelpCommand('help', $this); } else { return null; } } }
/** * Returns the HTML for the Target Locale setting. * * @access protected * @return string|null */ protected function getTargetLocaleFieldHtml($elementType, $targetLocale, $elementName) { if (craft()->hasPackage(CraftPackage::Localize) && $elementType->isLocalized()) { $localeOptions = array(array('label' => Craft::t('Same as source'), 'value' => null)); foreach (craft()->i18n->getSiteLocales() as $locale) { $localeOptions[] = array('label' => $locale->getName(), 'value' => $locale->getId()); } return craft()->templates->renderMacro('_includes/forms', 'selectField', array(array('label' => Craft::t('Target Locale'), 'instructions' => Craft::t('Which locale do you want to select {type} in?', array('type' => StringHelper::toLowerCase($elementName))), 'id' => 'targetLocale', 'name' => 'targetLocale', 'options' => $localeOptions, 'value' => $targetLocale))); } }
/** * Get icon path for a given extension * * @param $ext * * @return string */ private function _getIconPath($ext) { $sourceIconPath = craft()->path->getResourcesPath() . 'images/file.svg'; $extLength = mb_strlen($ext); if ($extLength > 5) { // Too long; just use the blank file icon return $sourceIconPath; } // See if the icon already exists $iconPath = craft()->path->getAssetsIconsPath() . StringHelper::toLowerCase($ext) . '.svg'; if (IOHelper::fileExists($iconPath)) { return $iconPath; } // Create a new one $svgContents = IOHelper::getFileContents($sourceIconPath); $textSize = $extLength <= 3 ? '26' : ($extLength == 4 ? '22' : '18'); $textNode = '<text x="50" y="73" text-anchor="middle" font-family="sans-serif" fill="#8F98A3" ' . 'font-size="' . $textSize . '">' . StringHelper::toUpperCase($ext) . '</text>'; $svgContents = str_replace('<!-- EXT -->', $textNode, $svgContents); IOHelper::writeToFile($iconPath, $svgContents); return $iconPath; }
/** * Returns the content table name for a given Matrix field. * * @param FieldModel $matrixField The Matrix field. * @param bool $useOldHandle Whether the method should use the field’s old handle when determining the table * name (e.g. to get the existing table name, rather than the new one). * * @return string|false The table name, or `false` if $useOldHandle was set to `true` and there was no old handle. */ public function getContentTableName(FieldModel $matrixField, $useOldHandle = false) { $name = ''; do { if ($useOldHandle) { if (!$matrixField->oldHandle) { return false; } $handle = $matrixField->oldHandle; } else { $handle = $matrixField->handle; } $name = '_' . StringHelper::toLowerCase($handle) . $name; } while ($matrixField = $this->getParentMatrixField($matrixField)); return 'matrixcontent' . $name; }
/** * Function that (almost) mimics Craft's inner slugify process. * But... we allow forward slashes to stay, so we can create full uri's. * * @param string $slug * * @return string */ public function slugify($slug) { // Remove HTML tags $slug = preg_replace('/<(.*?)>/u', '', $slug); // Remove inner-word punctuation. $slug = preg_replace('/[\'"‘’“”\\[\\]\\(\\)\\{\\}:]/u', '', $slug); if (craft()->config->get('allowUppercaseInSlug') === false) { // Make it lowercase $slug = StringHelper::toLowerCase($slug); } // Get the "words". Split on anything that is not a unicode letter or number. Periods, underscores, hyphens and forward slashes get a pass. preg_match_all('/[\\p{L}\\p{N}\\.\\/_-]+/u', $slug, $words); $words = ArrayHelper::filterEmptyStringsFromArray($words[0]); $slug = implode(craft()->config->get('slugWordSeparator'), $words); return $slug; }
/** * Returns whether an image can have EXIF information embedded. * * @param string $filePath The path to the image * * @return bool */ public static function canHaveExifData($filePath) { $extension = IOHelper::getExtension($filePath); return in_array(StringHelper::toLowerCase($extension), array('jpg', 'jpeg', 'tiff')); }
/** * Used for creating a new migration, for either Craft or a plugin. * * yiic migrate create MigrationDescription PluginHandle * * If PluginHandle is omitted, the migration is created for Craft in craft/app/migrations. If it is available, the * migration is created in craft/plugins/PluginHandle/migrations. * * The migration description can only contain letters, digits and/or underscore characters. * * @param array $args The arguments passed in. * * @return int */ public function actionCreate($args) { $pluginHandle = false; if (isset($args[1])) { // See if this is a plugin $plugin = craft()->plugins->getPlugin($args[1]); if ($plugin) { $name = $args[0]; $pluginHandle = $args[1]; } else { $name = $args[1]; } if (!preg_match('/^\\w+$/', $name)) { echo "Error: The name of the migration must contain letters, digits and/or underscore characters only.\n"; return 1; } $fullName = 'm' . gmdate('ymd_His') . '_' . $name; $migrationNameDesc = 'mYYMMDD_HHMMSS_migrationName'; } else { $this->usageError('Please provide a name for the new migration.'); return 1; } if ($pluginHandle) { $this->_validatePlugin($pluginHandle); if (!preg_match('/^\\w+$/', $pluginHandle)) { echo "Error: The name of the plugin must contain letters, digits and/or underscore characters only.\n"; return 1; } $fullName = 'm' . gmdate('ymd_His') . '_' . StringHelper::toLowerCase($pluginHandle) . '_' . $name; $migrationNameDesc = 'mYYMMDD_HHMMSS_pluginHandle_migrationName'; // The plugin path should always be the plugin's migration directory. $path = craft()->path->getMigrationsPath($pluginHandle); } else { // The plugin path for Craft can vary. $path = rtrim(IOHelper::normalizePathSeparators($args[0]), '/') . '/'; } $content = strtr(craft()->migrations->getTemplate(), array('{ClassName}' => $fullName, '{MigrationNameDesc}' => $migrationNameDesc)); $file = $path . $fullName . '.php'; if ($this->confirm("Create new migration '{$file}'?")) { IOHelper::writeToFile($file, $content); echo "New migration created successfully.\n"; } }
/** * Returns the target app language. * * @param bool Whether the user's preferred language should be used * @return string|null */ public function getTargetLanguage($useUserLanguage = true) { if ($this->isInstalled()) { // Will any locale validation be necessary here? if ($useUserLanguage || defined('CRAFT_LOCALE')) { if ($useUserLanguage) { $locale = 'auto'; } else { $locale = StringHelper::toLowerCase(CRAFT_LOCALE); } // Get the list of actual site locale IDs $siteLocaleIds = craft()->i18n->getSiteLocaleIds(); // Is it set to "auto"? if ($locale == 'auto') { // Prevents a PHP notice in case the session failed to start, for whatever reason. if (craft()->getComponent('userSession', false)) { // Place this within a try/catch in case userSession is being fussy. try { // If the user is logged in *and* has a primary language set, use that $user = craft()->userSession->getUser(); if ($user && $user->preferredLocale) { return $user->preferredLocale; } } catch (\Exception $e) { Craft::log("Tried to determine the user's preferred locale, but got this exception: " . $e->getMessage(), LogLevel::Error); } } // Is there a default CP language? if ($defaultCpLanguage = craft()->config->get('defaultCpLanguage')) { // Make sure it's one of the site locales $defaultCpLanguage = StringHelper::toLowerCase($defaultCpLanguage); if (in_array($defaultCpLanguage, $siteLocaleIds)) { return $defaultCpLanguage; } } // Otherwise check if the browser's preferred language matches any of the site locales $browserLanguages = craft()->request->getBrowserLanguages(); if ($browserLanguages) { foreach ($browserLanguages as $language) { if (in_array($language, $siteLocaleIds)) { return $language; } } } } else { if (in_array($locale, $siteLocaleIds)) { return $locale; } } } // Use the primary site locale by default return craft()->i18n->getPrimarySiteLocaleId(); } else { return $this->_getFallbackLanguage(); } }
/** * Return a file's kind by extension. * * @param string $extension * * @return int|string */ public static function getFileKind($extension) { $extension = StringHelper::toLowerCase($extension); $fileKinds = static::getFileKinds(); foreach ($fileKinds as $kind => $info) { if (in_array($extension, $info['extensions'])) { return $kind; } } return 'unknown'; }
/** * Preps a {@link DbCommand} object for querying for elements, based on a given element criteria. * * @param ElementCriteriaModel &$criteria The element criteria model * @param string &$contentTable The content table that should be joined in. (This variable will * actually get defined by buildElementsQuery(), and is passed by * reference so whatever’s calling the method will have access to its * value.) * @param array &$fieldColumns Info about the content field columns being selected. (This variable * will actually get defined by buildElementsQuery(), and is passed by * reference so whatever’s calling the method will have access to its * value.) * * @return DbCommand|false The DbCommand object, or `false` if the method was able to determine ahead of time that * there’s no chance any elements are going to be found with the given parameters. */ public function buildElementsQuery(&$criteria = null, &$contentTable = null, &$fieldColumns = null) { if (!$criteria instanceof ElementCriteriaModel) { $criteria = $this->getCriteria('Entry', $criteria); } $elementType = $criteria->getElementType(); if (!$elementType->isLocalized()) { // The criteria *must* be set to the primary locale $criteria->locale = craft()->i18n->getPrimarySiteLocaleId(); } else { if (!$criteria->locale) { // Default to the current app locale $criteria->locale = craft()->language; } } // Set up the query // --------------------------------------------------------------------- $query = craft()->db->createCommand()->select('elements.id, elements.type, elements.enabled, elements.archived, elements.dateCreated, elements.dateUpdated, elements_i18n.slug, elements_i18n.uri, elements_i18n.enabled AS localeEnabled')->from('elements elements')->join('elements_i18n elements_i18n', 'elements_i18n.elementId = elements.id')->where('elements_i18n.locale = :locale', array(':locale' => $criteria->locale))->group('elements.id'); if ($elementType->hasContent()) { $contentTable = $elementType->getContentTableForElementsQuery($criteria); if ($contentTable) { $contentCols = 'content.id AS contentId'; if ($elementType->hasTitles()) { $contentCols .= ', content.title'; } // TODO: Replace this with a call to getFieldsForElementsQuery() in 3.0 $fieldColumns = $elementType->getContentFieldColumnsForElementsQuery($criteria); foreach ($fieldColumns as $column) { $contentCols .= ', content.' . $column['column']; } $query->addSelect($contentCols); $query->join($contentTable . ' content', 'content.elementId = elements.id'); $query->andWhere('content.locale = :locale'); } } // Basic element params // --------------------------------------------------------------------- // If the 'id' parameter is set to any empty value besides `null`, don't return anything if ($criteria->id !== null && empty($criteria->id)) { return false; } if ($criteria->id) { $query->andWhere(DbHelper::parseParam('elements.id', $criteria->id, $query->params)); } if ($criteria->archived) { $query->andWhere('elements.archived = 1'); } else { $query->andWhere('elements.archived = 0'); if ($criteria->status) { $statusConditions = array(); $statuses = ArrayHelper::stringToArray($criteria->status); foreach ($statuses as $status) { $status = StringHelper::toLowerCase($status); // Is this a supported status? if (in_array($status, array_keys($elementType->getStatuses()))) { if ($status == BaseElementModel::ENABLED) { $statusConditions[] = 'elements.enabled = 1'; } else { if ($status == BaseElementModel::DISABLED) { $statusConditions[] = 'elements.enabled = 0'; } else { $elementStatusCondition = $elementType->getElementQueryStatusCondition($query, $status); if ($elementStatusCondition) { $statusConditions[] = $elementStatusCondition; } else { if ($elementStatusCondition === false) { return false; } } } } } } if ($statusConditions) { if (count($statusConditions) == 1) { $statusCondition = $statusConditions[0]; } else { array_unshift($statusConditions, 'or'); $statusCondition = $statusConditions; } $query->andWhere($statusCondition); } } } if ($criteria->dateCreated) { $query->andWhere(DbHelper::parseDateParam('elements.dateCreated', $criteria->dateCreated, $query->params)); } if ($criteria->dateUpdated) { $query->andWhere(DbHelper::parseDateParam('elements.dateUpdated', $criteria->dateUpdated, $query->params)); } if ($elementType->hasTitles() && $criteria->title) { $query->andWhere(DbHelper::parseParam('content.title', $criteria->title, $query->params)); } // i18n params // --------------------------------------------------------------------- if ($criteria->slug) { $query->andWhere(DbHelper::parseParam('elements_i18n.slug', $criteria->slug, $query->params)); } if ($criteria->uri) { $query->andWhere(DbHelper::parseParam('elements_i18n.uri', $criteria->uri, $query->params)); } if ($criteria->localeEnabled) { $query->andWhere('elements_i18n.enabled = 1'); } // Relational params // --------------------------------------------------------------------- // Convert the old childOf and parentOf params to the relatedTo param // childOf(element) => relatedTo({ source: element }) // parentOf(element) => relatedTo({ target: element }) if (!$criteria->relatedTo && ($criteria->childOf || $criteria->parentOf)) { $relatedTo = array('and'); if ($criteria->childOf) { $relatedTo[] = array('sourceElement' => $criteria->childOf, 'field' => $criteria->childField); } if ($criteria->parentOf) { $relatedTo[] = array('targetElement' => $criteria->parentOf, 'field' => $criteria->parentField); } $criteria->relatedTo = $relatedTo; } if ($criteria->relatedTo) { $relationParamParser = new ElementRelationParamParser(); $relConditions = $relationParamParser->parseRelationParam($criteria->relatedTo, $query); if ($relConditions === false) { return false; } $query->andWhere($relConditions); // If there's only one relation criteria and it's specifically for grabbing target elements, allow the query // to order by the relation sort order if ($relationParamParser->isRelationFieldQuery()) { $query->addSelect('sources1.sortOrder'); } } // Give field types a chance to make changes // --------------------------------------------------------------------- if ($elementType->hasContent() && $contentTable) { $contentService = craft()->content; $originalFieldColumnPrefix = $contentService->fieldColumnPrefix; // TODO: $fields should already be defined by now in Craft 3.0 $fields = $elementType->getFieldsForElementsQuery($criteria); $extraCriteriaAttributes = $criteria->getExtraAttributeNames(); foreach ($fields as $field) { $fieldType = $field->getFieldType(); if ($fieldType) { // Was this field's parameter set on the criteria model? if (in_array($field->handle, $extraCriteriaAttributes)) { $fieldCriteria = $criteria->{$field->handle}; } else { $fieldCriteria = null; } // Set the field's column prefix on ContentService if ($field->columnPrefix) { $contentService->fieldColumnPrefix = $field->columnPrefix; } $fieldTypeResponse = $fieldType->modifyElementsQuery($query, $fieldCriteria); // Set it back $contentService->fieldColumnPrefix = $originalFieldColumnPrefix; // Need to bail early? if ($fieldTypeResponse === false) { return false; } } } } // Give the element type a chance to make changes // --------------------------------------------------------------------- if ($elementType->modifyElementsQuery($query, $criteria) === false) { return false; } // Structure params // --------------------------------------------------------------------- if ($query->isJoined('structureelements')) { $query->addSelect('structureelements.root, structureelements.lft, structureelements.rgt, structureelements.level'); if ($criteria->ancestorOf) { if (!$criteria->ancestorOf instanceof BaseElementModel) { $criteria->ancestorOf = craft()->elements->getElementById($criteria->ancestorOf, $elementType->getClassHandle(), $criteria->locale); if (!$criteria->ancestorOf) { return false; } } if ($criteria->ancestorOf) { $query->andWhere(array('and', 'structureelements.lft < :ancestorOf_lft', 'structureelements.rgt > :ancestorOf_rgt', 'structureelements.root = :ancestorOf_root'), array(':ancestorOf_lft' => $criteria->ancestorOf->lft, ':ancestorOf_rgt' => $criteria->ancestorOf->rgt, ':ancestorOf_root' => $criteria->ancestorOf->root)); if ($criteria->ancestorDist) { $query->andWhere('structureelements.level >= :ancestorOf_level', array(':ancestorOf_level' => $criteria->ancestorOf->level - $criteria->ancestorDist)); } } } if ($criteria->descendantOf) { if (!$criteria->descendantOf instanceof BaseElementModel) { $criteria->descendantOf = craft()->elements->getElementById($criteria->descendantOf, $elementType->getClassHandle(), $criteria->locale); if (!$criteria->descendantOf) { return false; } } if ($criteria->descendantOf) { $query->andWhere(array('and', 'structureelements.lft > :descendantOf_lft', 'structureelements.rgt < :descendantOf_rgt', 'structureelements.root = :descendantOf_root'), array(':descendantOf_lft' => $criteria->descendantOf->lft, ':descendantOf_rgt' => $criteria->descendantOf->rgt, ':descendantOf_root' => $criteria->descendantOf->root)); if ($criteria->descendantDist) { $query->andWhere('structureelements.level <= :descendantOf_level', array(':descendantOf_level' => $criteria->descendantOf->level + $criteria->descendantDist)); } } } if ($criteria->siblingOf) { if (!$criteria->siblingOf instanceof BaseElementModel) { $criteria->siblingOf = craft()->elements->getElementById($criteria->siblingOf, $elementType->getClassHandle(), $criteria->locale); if (!$criteria->siblingOf) { return false; } } if ($criteria->siblingOf) { $query->andWhere(array('and', 'structureelements.level = :siblingOf_level', 'structureelements.root = :siblingOf_root', 'structureelements.elementId != :siblingOf_elementId'), array(':siblingOf_level' => $criteria->siblingOf->level, ':siblingOf_root' => $criteria->siblingOf->root, ':siblingOf_elementId' => $criteria->siblingOf->id)); if ($criteria->siblingOf->level != 1) { $parent = $criteria->siblingOf->getParent(); if ($parent) { $query->andWhere(array('and', 'structureelements.lft > :siblingOf_lft', 'structureelements.rgt < :siblingOf_rgt'), array(':siblingOf_lft' => $parent->lft, ':siblingOf_rgt' => $parent->rgt)); } else { return false; } } } } if ($criteria->prevSiblingOf) { if (!$criteria->prevSiblingOf instanceof BaseElementModel) { $criteria->prevSiblingOf = craft()->elements->getElementById($criteria->prevSiblingOf, $elementType->getClassHandle(), $criteria->locale); if (!$criteria->prevSiblingOf) { return false; } } if ($criteria->prevSiblingOf) { $query->andWhere(array('and', 'structureelements.level = :prevSiblingOf_level', 'structureelements.rgt = :prevSiblingOf_rgt', 'structureelements.root = :prevSiblingOf_root'), array(':prevSiblingOf_level' => $criteria->prevSiblingOf->level, ':prevSiblingOf_rgt' => $criteria->prevSiblingOf->lft - 1, ':prevSiblingOf_root' => $criteria->prevSiblingOf->root)); } } if ($criteria->nextSiblingOf) { if (!$criteria->nextSiblingOf instanceof BaseElementModel) { $criteria->nextSiblingOf = craft()->elements->getElementById($criteria->nextSiblingOf, $elementType->getClassHandle(), $criteria->locale); if (!$criteria->nextSiblingOf) { return false; } } if ($criteria->nextSiblingOf) { $query->andWhere(array('and', 'structureelements.level = :nextSiblingOf_level', 'structureelements.lft = :nextSiblingOf_lft', 'structureelements.root = :nextSiblingOf_root'), array(':nextSiblingOf_level' => $criteria->nextSiblingOf->level, ':nextSiblingOf_lft' => $criteria->nextSiblingOf->rgt + 1, ':nextSiblingOf_root' => $criteria->nextSiblingOf->root)); } } if ($criteria->positionedBefore) { if (!$criteria->positionedBefore instanceof BaseElementModel) { $criteria->positionedBefore = craft()->elements->getElementById($criteria->positionedBefore, $elementType->getClassHandle(), $criteria->locale); if (!$criteria->positionedBefore) { return false; } } if ($criteria->positionedBefore) { $query->andWhere(array('and', 'structureelements.rgt < :positionedBefore_rgt', 'structureelements.root = :positionedBefore_root'), array(':positionedBefore_rgt' => $criteria->positionedBefore->lft, ':positionedBefore_root' => $criteria->positionedBefore->root)); } } if ($criteria->positionedAfter) { if (!$criteria->positionedAfter instanceof BaseElementModel) { $criteria->positionedAfter = craft()->elements->getElementById($criteria->positionedAfter, $elementType->getClassHandle(), $criteria->locale); if (!$criteria->positionedAfter) { return false; } } if ($criteria->positionedAfter) { $query->andWhere(array('and', 'structureelements.lft > :positionedAfter_lft', 'structureelements.root = :positionedAfter_root'), array(':positionedAfter_lft' => $criteria->positionedAfter->rgt, ':positionedAfter_root' => $criteria->positionedAfter->root)); } } if ($criteria->level || $criteria->depth) { // TODO: 'depth' is deprecated; use 'level' instead. $level = $criteria->level ? $criteria->level : $criteria->depth; $query->andWhere(DbHelper::parseParam('structureelements.level', $level, $query->params)); } } // Search // --------------------------------------------------------------------- if ($criteria->search) { $elementIds = $this->_getElementIdsFromQuery($query); $scoredSearchResults = $criteria->order == 'score'; $filteredElementIds = craft()->search->filterElementIdsByQuery($elementIds, $criteria->search, $scoredSearchResults); // No results? if (!$filteredElementIds) { return array(); } $query->andWhere(array('in', 'elements.id', $filteredElementIds)); if ($scoredSearchResults) { // Order the elements in the exact order that SearchService returned them in $query->order(craft()->db->getSchema()->orderByColumnValues('elements.id', $filteredElementIds)); } } return $query; }
/** * @inheritDoc IFieldType::prepValueFromPost() * * @param mixed $value * * @return mixed */ public function prepValueFromPost($value) { $dataFiles = array(); // Grab data strings if (isset($value['data']) && is_array($value['data'])) { foreach ($value['data'] as $index => $dataString) { if (preg_match('/^data:(?<type>[a-z0-9]+\\/[a-z0-9]+);base64,(?<data>.+)/i', $dataString, $matches)) { $type = $matches['type']; $data = base64_decode($matches['data']); if (!$data) { continue; } if (!empty($value['filenames'][$index])) { $filename = $value['filenames'][$index]; } else { $extension = FileHelper::getExtensionByMimeType($type); $filename = 'Uploaded file.' . $extension; } $dataFiles[] = array('filename' => $filename, 'data' => $data); } } } // Remove these so they don't interfere. if (isset($value['data']) && isset($value['filenames'])) { unset($value['data'], $value['filenames']); } $uploadedFiles = array(); // See if we have uploaded file(s). $contentPostLocation = $this->getContentPostLocation(); if ($contentPostLocation) { $files = UploadedFile::getInstancesByName($contentPostLocation); foreach ($files as $file) { $uploadedFiles[] = array('filename' => $file->getName(), 'location' => $file->getTempName()); } } // See if we have to validate against fileKinds $settings = $this->getSettings(); $allowedExtensions = false; if (isset($settings->restrictFiles) && !empty($settings->restrictFiles) && !empty($settings->allowedKinds)) { $allowedExtensions = static::_getAllowedExtensions($settings->allowedKinds); } if (is_array($allowedExtensions)) { foreach ($dataFiles as $file) { $extension = StringHelper::toLowerCase(IOHelper::getExtension($file['filename'])); if (!in_array($extension, $allowedExtensions)) { $this->_failedFiles[] = $file['filename']; } } foreach ($uploadedFiles as $file) { $extension = StringHelper::toLowerCase(IOHelper::getExtension($file['filename'])); if (!in_array($extension, $allowedExtensions)) { $this->_failedFiles[] = $file['filename']; } } } if (!empty($this->_failedFiles)) { return true; } // If we got here either there are no restrictions or all files are valid so let's turn them into Assets // Unless there are no files at all. if (empty($value) && empty($dataFiles) && empty($uploadedFiles)) { return array(); } if (empty($value)) { $value = array(); } $fileIds = array(); if (!empty($dataFiles) || !empty($uploadedFiles)) { $targetFolderId = $this->_determineUploadFolderId($settings); foreach ($dataFiles as $file) { $tempPath = AssetsHelper::getTempFilePath($file['filename']); IOHelper::writeToFile($tempPath, $file['data']); $response = craft()->assets->insertFileByLocalPath($tempPath, $file['filename'], $targetFolderId); $fileIds[] = $response->getDataItem('fileId'); IOHelper::deleteFile($tempPath, true); } foreach ($uploadedFiles as $file) { $tempPath = AssetsHelper::getTempFilePath($file['filename']); move_uploaded_file($file['location'], $tempPath); $response = craft()->assets->insertFileByLocalPath($tempPath, $file['filename'], $targetFolderId); $fileIds[] = $response->getDataItem('fileId'); IOHelper::deleteFile($tempPath, true); } } $fileIds = array_merge($value, $fileIds); // Make it look like the actual POST data contained these file IDs as well, // so they make it into entry draft/version data $this->element->setRawPostContent($this->model->handle, $fileIds); return $fileIds; }
/** * Rename a folder. * * @param AssetFolderModel $folder The assetFolderModel representing the name of the folder to rename. * @param string $newName The new name of the folder. * * @throws Exception * @return AssetOperationResponseModel */ public function renameFolder(AssetFolderModel $folder, $newName) { $parentFolder = craft()->assets->getFolderById($folder->parentId); if (!$parentFolder) { throw new Exception(Craft::t("Cannot rename folder “{folder}”!", array('folder' => $folder->name))); } // Allow this for changing the case if (!(StringHelper::toLowerCase($newName) == StringHelper::toLowerCase($folder->name)) && $this->folderExists($parentFolder, $newName)) { throw new Exception(Craft::t("Folder “{folder}” already exists there.", array('folder' => $newName))); } // Try to rename the folder in the source if (!$this->renameSourceFolder($folder, $newName)) { throw new Exception(Craft::t("Cannot rename folder “{folder}”!", array('folder' => $folder->name))); } $oldFullPath = $folder->path; $newFullPath = IOHelper::getParentFolderPath($folder->path) . $newName . '/'; // Find all folders with affected fullPaths and update them. $folders = craft()->assets->getAllDescendantFolders($folder); foreach ($folders as $folderModel) { $folderModel->path = preg_replace('#^' . $oldFullPath . '#', $newFullPath, $folderModel->path); craft()->assets->storeFolder($folderModel); } // Now change the affected folder $folder->name = $newName; $folder->path = $newFullPath; craft()->assets->storeFolder($folder); // All set, Scotty! $response = new AssetOperationResponseModel(); return $response->setSuccess()->setDataItem('newName', $newName); }
/** * Adds support for array('column' => 'value') conditional syntax. Supports nested conditionals, e.g. * array('or', array('column' => 'value'), array('column2' => 'value2')) * * @param mixed $conditions * @param array &$params * * @return mixed */ private function _normalizeConditions($conditions, &$params = array()) { if (!is_array($conditions)) { return $conditions; } else { if ($conditions === array()) { return ''; } } $normalizedConditions = array(); // Find any key/value pairs and convert them to the CDbCommand's conditional syntax foreach ($conditions as $key => $value) { if (!is_numeric($key)) { $param = ':p' . StringHelper::randomString(9); $normalizedConditions[] = $this->getConnection()->quoteColumnName($key) . '=' . $param; $params[$param] = $value; unset($conditions[$key]); } else { $conditions[$key] = $this->_normalizeConditions($value, $params); } } if ($normalizedConditions) { // Were there normal conditions in there as well? if ($conditions) { // Is this already an AND conditional? if (StringHelper::toLowerCase($conditions[0]) == 'and') { // Just merge our normalized conditions into the $conditions $conditions = array_merge($conditions, $normalizedConditions); } else { // Append the normalized conditions as nested AND conditions array_unshift($normalizedConditions, 'and'); $conditions[] = $normalizedConditions; } } else { if (count($normalizedConditions) == 1) { $conditions = $normalizedConditions[0]; } else { array_unshift($normalizedConditions, 'and'); $conditions = $normalizedConditions; } } } return $conditions; }
/** * Extracts the operator from a DB param and returns it. * * @param string &$value * * @return string */ private static function _parseParamOperator(&$value) { foreach (static::$_operators as $testOperator) { // Does the value start with this operator? $operatorLength = mb_strlen($testOperator); if (strncmp(StringHelper::toLowerCase($value), $testOperator, $operatorLength) == 0) { $value = mb_substr($value, $operatorLength); if ($testOperator == 'not ') { return '!='; } else { return $testOperator; } } } return '='; }
/** * Returns the path to the craft/app/migrations/ folder, or the path to a plugin’s migrations/ folder. * * @param string $pluginHandle The plugin handle whose migrations/ folder should be returned. Defaults to `null`. * * @return string The path to the migrations/ folder. */ public function getMigrationsPath($pluginHandle = null) { if ($pluginHandle) { return $this->getPluginsPath() . StringHelper::toLowerCase($pluginHandle) . '/migrations/'; } return $this->getAppPath() . 'migrations/'; }
/** * Gets the current encoding of the given string. * * @param string $string * * @return string */ public static function getEncoding($string) { return StringHelper::toLowerCase(mb_detect_encoding($string, mb_detect_order(), true)); }