/** * Fetches the installed package info from Elliott. */ public function actionFetchPackageInfo() { $this->requireAjaxRequest(); $etResponse = craft()->et->fetchPackageInfo(); if ($etResponse) { // Make sure we've got a valid license key (mismatched domain is OK for these purposes) if ($etResponse->licenseKeyStatus != LicenseKeyStatus::Invalid) { $packages = $etResponse->data; // Include which packages are actually licensed foreach ($etResponse->licensedPackages as $packageName) { $packages[$packageName]['licensed'] = true; } // Include which packages are in trial foreach ($etResponse->packageTrials as $packageName => $expiryDate) { $currentTime = DateTimeHelper::currentUTCDateTime(); $diff = $expiryDate - $currentTime->getTimestamp(); $daysLeft = round($diff / 86400); // 60 * 60 * 24 $packages[$packageName]['trial'] = true; $packages[$packageName]['daysLeftInTrial'] = $daysLeft; } $this->returnJson(array('success' => true, 'packages' => $packages)); } else { $this->returnErrorJson(Craft::t('Your license key is invalid.')); } } else { $this->returnErrorJson(Craft::t('Craft is unable to fetch package info at this time.')); } }
/** * @static * @return string */ public static function currentTimeForDb() { // Eventually this will return the time in the appropriate database format for MySQL, Postgre, etc. // For now, it's MySQL only. $date = DateTimeHelper::currentUTCDateTime(); return $date->format(DateTime::MYSQL_DATETIME, DateTime::UTC); }
/** * Returns the next active announcement. * * @param string $timeInAdvance * * @return AnnouncementModel */ public function getNextAnnouncement($timeInAdvance = '0') { $time = DateTimeHelper::currentUTCDateTime(); $time->modify('+' . $timeInAdvance); $announcementRecord = craft()->db->createCommand()->select('*')->from('maintenance_announcements')->where(array('and', array('or', 'blockSite = 1', 'blockCp = 1'), 'startDate <= :time', array('or', 'endDate >= :now', 'endDate IS NULL')), array(':now' => DateTimeHelper::formatTimeForDb(), ':time' => DateTimeHelper::formatTimeForDb($time)))->order('startDate desc')->queryRow(); if ($announcementRecord) { return Maintenance_AnnouncementModel::populateModel($announcementRecord); } }
/** * Shuns a CP alert for 24 hours. * * @return null */ public function actionShunCpAlert() { $this->requireAjaxRequest(); craft()->userSession->requirePermission('accessCp'); $message = craft()->request->getRequiredPost('message'); $user = craft()->userSession->getUser(); $currentTime = DateTimeHelper::currentUTCDateTime(); $tomorrow = $currentTime->add(new DateInterval('P1D')); if (craft()->users->shunMessageForUser($user->id, $message, $tomorrow)) { $this->returnJson(array('success' => true)); } else { $this->returnErrorJson(Craft::t('An unknown error occurred.')); } }
/** * Creates a new token and returns it. * * @param mixed $route Where matching requests should be routed to. If you want them to be routed to a * controller action, pass: * `array('action' => "controller/action", 'params' => array('foo' => 'bar'))`. * @param int|null $usageLimit The maximum number of times this token can be used. Defaults to no limit. * @param DateTime|null $expiryDate The date that the token expires. Defaults to the 'defaultTokenDuration' config * setting. * * @return string|false The generated token, or `false` if there was an error. */ public function createToken($route, $usageLimit = null, $expiryDate = null) { if (!$expiryDate) { $expiryDate = DateTimeHelper::currentUTCDateTime(); $expiryDate->add(new DateInterval(craft()->config->get('defaultTokenDuration'))); } $tokenRecord = new TokenRecord(); $tokenRecord->token = craft()->security->generateRandomString(32); $tokenRecord->route = $route; if ($usageLimit) { $tokenRecord->usageCount = 0; $usageLimit->usageLimit = $usageLimit; } $tokenRecord->expiryDate = $expiryDate; $success = $tokenRecord->save(); if ($success) { return $tokenRecord->token; } else { return false; } }
/** * Returns a list of global variables to add to the existing list. * * @return array An array of global variables */ public function getGlobals() { // Keep the 'blx' variable around for now $craftVariable = new CraftVariable(); $globals['craft'] = $craftVariable; $globals['blx'] = $craftVariable; $globals['now'] = DateTimeHelper::currentUTCDateTime(); $globals['loginUrl'] = UrlHelper::getUrl(craft()->config->getLoginPath()); $globals['logoutUrl'] = UrlHelper::getUrl(craft()->config->getLogoutPath()); if (craft()->isInstalled() && !craft()->updates->isCraftDbMigrationNeeded()) { $globals['siteName'] = craft()->getSiteName(); $globals['siteUrl'] = craft()->getSiteUrl(); $globals['currentUser'] = craft()->userSession->getUser(); // Keep 'user' around so long as it's not hurting anyone. // Technically deprecated, though. $globals['user'] = $globals['currentUser']; if (craft()->request->isSiteRequest()) { foreach (craft()->globals->getAllSets() as $globalSet) { $globals[$globalSet->handle] = $globalSet; } } } else { $globals['siteName'] = null; $globals['siteUrl'] = null; $globals['user'] = null; } if (craft()->request->isCpRequest()) { $globals['CraftEdition'] = craft()->getEdition(); $globals['CraftPersonal'] = Craft::Personal; $globals['CraftClient'] = Craft::Client; $globals['CraftPro'] = Craft::Pro; } return $globals; }
/** * @param $rememberMe * @return int */ private function _getSessionDuration($rememberMe) { if ($rememberMe) { $duration = craft()->config->get('rememberedUserSessionDuration'); } else { $duration = craft()->config->get('userSessionDuration'); } // Calculate how long the session should last. if ($duration) { $interval = new DateInterval($duration); $expire = DateTimeHelper::currentUTCDateTime(); $currentTimeStamp = $expire->getTimestamp(); $futureTimeStamp = $expire->add($interval)->getTimestamp(); $seconds = $futureTimeStamp - $currentTimeStamp; } else { $seconds = 0; } return $seconds; }
/** * @return array */ protected function defineAttributes() { return array('title' => array(AttributeType::Name, 'required' => true), 'body' => array(AttributeType::String, 'required' => true), 'command' => AttributeType::String, 'schedule' => array(AttributeType::DateTime, 'default' => DateTimeHelper::currentUTCDateTime())); }
/** * Insert a file into a folder by it's local path. * * @param string $localFilePath The local file path of the file to insert. * @param AssetFolderModel $folder The assetFolderModel where the file should be uploaded to. * @param string $filename The name of the file to insert. * @param bool $preventConflicts If set to true, will ensure that a conflict is not encountered by * checking the file name prior insertion. * * @return AssetOperationResponseModel */ public function insertFileByPath($localFilePath, AssetFolderModel $folder, $filename, $preventConflicts = false) { // Fire an 'onBeforeUploadAsset' event $event = new Event($this, array('path' => $localFilePath, 'folder' => $folder, 'filename' => $filename)); craft()->assets->onBeforeUploadAsset($event); if ($event->performAction) { // We hate Javascript and PHP in our image files. if (IOHelper::getFileKind(IOHelper::getExtension($localFilePath)) == 'image' && ImageHelper::isImageManipulatable(IOHelper::getExtension($localFilePath)) && IOHelper::getExtension($localFilePath) != 'svg') { craft()->images->cleanImage($localFilePath); } $mobileUpload = false; if (IOHelper::getFileName($filename, false) == "image" && craft()->request->isMobileBrowser(true)) { $mobileUpload = true; $date = DateTimeHelper::currentUTCDateTime(); $filename = "image_" . $date->format('Ymd_His') . "." . IOHelper::getExtension($filename); } if ($preventConflicts) { $newFileName = $this->getNameReplacementInFolder($folder, $filename); $response = $this->insertFileInFolder($folder, $localFilePath, $newFileName); } else { $response = $this->insertFileInFolder($folder, $localFilePath, $filename); // Naming conflict. create a new file and ask the user what to do with it if ($response->isConflict()) { $newFileName = $this->getNameReplacementInFolder($folder, $filename); $conflictResponse = $response; $response = $this->insertFileInFolder($folder, $localFilePath, $newFileName); } } if ($response->isSuccess()) { $fileModel = new AssetFileModel(); $title = $fileModel->generateAttributeLabel(IOHelper::getFileName($filename, false)); // If there were double spaces, it's because the filename had a space followed by a // capital letter. We convert the space to a dash, but Yii thinks it's a new "word" // and adds another space. $fileModel->getContent()->title = str_replace(' ', ' ', $title); $filename = IOHelper::getFileName($response->getDataItem('filePath')); $fileModel->filename = IOHelper::getFileName($filename); $fileModel->sourceId = $this->model->id; $fileModel->folderId = $folder->id; $fileModel->kind = IOHelper::getFileKind(IOHelper::getExtension($filename)); $fileModel->size = filesize($localFilePath); $fileModel->dateModified = IOHelper::getLastTimeModified($localFilePath); if ($fileModel->kind == 'image') { list($width, $height) = ImageHelper::getImageSize($localFilePath); $fileModel->width = $width; $fileModel->height = $height; } if ($mobileUpload) { $fileModel->getContent()->title = Craft::t('Mobile Upload'); } craft()->assets->storeFile($fileModel); if (!$this->isSourceLocal() && $fileModel->kind == 'image') { craft()->assetTransforms->storeLocalSource($localFilePath, craft()->path->getAssetsImageSourcePath() . $fileModel->id . '.' . IOHelper::getExtension($fileModel->filename)); } // Check if we stored a conflict response originally - send that back then. if (isset($conflictResponse)) { $response = $conflictResponse; } $response->setDataItem('fileId', $fileModel->id); } } else { $response = new AssetOperationResponseModel(); $response->setError(Craft::t('The file upload was cancelled.')); } return $response; }
/** * @return array */ protected function defineAttributes() { return array_merge(parent::defineAttributes(), array('appId' => AttributeType::Number, 'title' => AttributeType::Name, 'body' => AttributeType::String, 'command' => AttributeType::String, 'schedule' => array(AttributeType::DateTime, 'default' => DateTimeHelper::currentUTCDateTime()))); }
/** * Returns a list of global variables to add to the existing list. * * @return array An array of global variables */ public function getGlobals() { // Keep the 'blx' variable around for now $craftVariable = new CraftVariable(); $globals['craft'] = $craftVariable; $globals['blx'] = $craftVariable; $globals['now'] = DateTimeHelper::currentUTCDateTime(); $globals['loginUrl'] = UrlHelper::getUrl(craft()->config->getLoginPath()); $globals['logoutUrl'] = UrlHelper::getUrl(craft()->config->getLogoutPath()); if (Craft::isInstalled()) { $globals['siteName'] = Craft::getSiteName(); $globals['siteUrl'] = Craft::getSiteUrl(); $globals['user'] = craft()->userSession->getUser(); if (craft()->request->isSiteRequest()) { foreach (craft()->globals->getAllSets() as $globalSet) { $globalSet->locale = craft()->language; $globals[$globalSet->handle] = $globalSet; } } } else { $globals['siteName'] = null; $globals['siteUrl'] = null; $globals['user'] = null; } return $globals; }
/** * Returns the remaining cooldown time for this user, if they've entered their password incorrectly too many times. * * @return DateInterval|null */ public function getRemainingCooldownTime() { if ($this->status == UserStatus::Locked) { $currentTime = DateTimeHelper::currentUTCDateTime(); $cooldownEnd = $this->getCooldownEndTime(); if ($currentTime < $cooldownEnd) { return $currentTime->diff($cooldownEnd); } } }
/** * Saves an entry. * * @param EntryModel $entry * @throws Exception * @return bool */ public function saveEntry(EntryModel $entry) { $isNewEntry = !$entry->id; // Entry data if (!$isNewEntry) { $entryRecord = EntryRecord::model()->with('element')->findById($entry->id); if (!$entryRecord) { throw new Exception(Craft::t('No entry exists with the ID “{id}”', array('id' => $entry->id))); } $elementRecord = $entryRecord->element; // if entry->sectionId is null and there is an entryRecord sectionId, we assume this is a front-end edit. if ($entry->sectionId === null && $entryRecord->sectionId) { $entry->sectionId = $entryRecord->sectionId; } } else { $entryRecord = new EntryRecord(); $elementRecord = new ElementRecord(); $elementRecord->type = ElementType::Entry; } $section = craft()->sections->getSectionById($entry->sectionId); if (!$section) { throw new Exception(Craft::t('No section exists with the ID “{id}”', array('id' => $entry->sectionId))); } $sectionLocales = $section->getLocales(); if (!isset($sectionLocales[$entry->locale])) { throw new Exception(Craft::t('The section “{section}” is not enabled for the locale {locale}', array('section' => $section->name, 'locale' => $entry->locale))); } $entryRecord->sectionId = $entry->sectionId; $entryRecord->authorId = $entry->authorId; $entryRecord->postDate = $entry->postDate; $entryRecord->expiryDate = $entry->expiryDate; if ($entry->enabled && !$entryRecord->postDate) { // Default the post date to the current date/time $entryRecord->postDate = $entry->postDate = DateTimeHelper::currentUTCDateTime(); } $entryRecord->validate(); $entry->addErrors($entryRecord->getErrors()); $elementRecord->enabled = $entry->enabled; $elementRecord->validate(); $entry->addErrors($elementRecord->getErrors()); // Entry locale data if ($entry->id) { $entryLocaleRecord = EntryLocaleRecord::model()->findByAttributes(array('entryId' => $entry->id, 'locale' => $entry->locale)); // If entry->slug is null and there is an entryLocaleRecord slug, we assume this is a front-end edit. if ($entry->slug === null && $entryLocaleRecord->slug) { $entry->slug = $entryLocaleRecord->slug; } } if (empty($entryLocaleRecord)) { $entryLocaleRecord = new EntryLocaleRecord(); $entryLocaleRecord->sectionId = $entry->sectionId; $entryLocaleRecord->locale = $entry->locale; } if ($entryLocaleRecord->isNewRecord() || $entry->slug != $entryLocaleRecord->slug) { $this->_generateEntrySlug($entry); $entryLocaleRecord->slug = $entry->slug; } $entryLocaleRecord->validate(); $entry->addErrors($entryLocaleRecord->getErrors()); // Element locale data if ($entry->id) { $elementLocaleRecord = ElementLocaleRecord::model()->findByAttributes(array('elementId' => $entry->id, 'locale' => $entry->locale)); } if (empty($elementLocaleRecord)) { $elementLocaleRecord = new ElementLocaleRecord(); $elementLocaleRecord->locale = $entry->locale; } if ($section->hasUrls && $entry->enabled) { // Make sure the section's URL format is valid. This shouldn't be possible due to section validation, // but it's not enforced by the DB, so anything is possible. $urlFormat = $sectionLocales[$entry->locale]->urlFormat; if (!$urlFormat || strpos($urlFormat, '{slug}') === false) { throw new Exception(Craft::t('The section “{section}” doesn’t have a valid URL Format.', array('section' => Craft::t($section->name)))); } $elementLocaleRecord->uri = craft()->templates->renderObjectTemplate($urlFormat, $entry); } else { $elementLocaleRecord->uri = null; } $elementLocaleRecord->validate(); $entry->addErrors($elementLocaleRecord->getErrors()); // Entry content $fieldLayout = $section->getFieldLayout(); $content = craft()->content->prepElementContentForSave($entry, $fieldLayout); $content->validate(); $entry->addErrors($content->getErrors()); if (!$entry->hasErrors()) { // Save the element record first $elementRecord->save(false); // Now that we have an element ID, save it on the other stuff if (!$entry->id) { $entry->id = $elementRecord->id; $entryRecord->id = $entry->id; } $entryRecord->save(false); $entryLocaleRecord->entryId = $entry->id; $elementLocaleRecord->elementId = $entry->id; $content->elementId = $entry->id; // Save the other records $entryLocaleRecord->save(false); $elementLocaleRecord->save(false); craft()->content->saveContent($content, false); // Update the search index craft()->search->indexElementAttributes($entry, $entry->locale); // Save a new version if (Craft::hasPackage(CraftPackage::PublishPro)) { craft()->entryRevisions->saveVersion($entry); } // Perform some post-save operations craft()->content->postSaveOperations($entry, $content); // Fire an 'onSaveEntry' event $this->onSaveEntry(new Event($this, array('entry' => $entry, 'isNewEntry' => $isNewEntry))); return true; } else { return false; } }
/** * Saves a new or existing entry. * * ```php * $entry = new EntryModel(); * $entry->sectionId = 10; * $entry->typeId = 1; * $entry->authorId = 5; * $entry->enabled = true; * * $entry->getContent()->title = "Hello World!"; * * $entry->setContentFromPost(array( * 'body' => "<p>I can’t believe I literally just called this “Hello World!”.</p>", * )); * * $success = craft()->entries->saveEntry($entry); * * if (!$success) * { * Craft::log('Couldn’t save the entry "'.$entry->title.'"', LogLevel::Error); * } * ``` * * @param EntryModel $entry The entry to be saved. * * @throws \Exception * @return bool Whether the entry was saved successfully. */ public function saveEntry(EntryModel $entry) { $isNewEntry = !$entry->id; $hasNewParent = $this->_checkForNewParent($entry); if ($hasNewParent) { if ($entry->parentId) { $parentEntry = $this->getEntryById($entry->parentId, $entry->locale); if (!$parentEntry) { throw new Exception(Craft::t('No entry exists with the ID “{id}”.', array('id' => $entry->parentId))); } } else { $parentEntry = null; } $entry->setParent($parentEntry); } // Get the entry record if (!$isNewEntry) { $entryRecord = EntryRecord::model()->findById($entry->id); if (!$entryRecord) { throw new Exception(Craft::t('No entry exists with the ID “{id}”.', array('id' => $entry->id))); } } else { $entryRecord = new EntryRecord(); } // Get the section $section = craft()->sections->getSectionById($entry->sectionId); if (!$section) { throw new Exception(Craft::t('No section exists with the ID “{id}”.', array('id' => $entry->sectionId))); } // Verify that the section is available in this locale $sectionLocales = $section->getLocales(); if (!isset($sectionLocales[$entry->locale])) { throw new Exception(Craft::t('The section “{section}” is not enabled for the locale {locale}', array('section' => $section->name, 'locale' => $entry->locale))); } // Set the entry data $entryType = $entry->getType(); $entryRecord->sectionId = $entry->sectionId; if ($section->type == SectionType::Single) { $entryRecord->authorId = $entry->authorId = null; $entryRecord->expiryDate = $entry->expiryDate = null; } else { $entryRecord->authorId = $entry->authorId; $entryRecord->postDate = $entry->postDate; $entryRecord->expiryDate = $entry->expiryDate; $entryRecord->typeId = $entryType->id; } if ($entry->enabled && !$entryRecord->postDate) { // Default the post date to the current date/time $entryRecord->postDate = $entry->postDate = DateTimeHelper::currentUTCDateTime(); } $entryRecord->validate(); $entry->addErrors($entryRecord->getErrors()); if ($entry->hasErrors()) { return false; } if (!$entryType->hasTitleField) { $entry->getContent()->title = craft()->templates->renderObjectTemplate($entryType->titleFormat, $entry); } $transaction = craft()->db->getCurrentTransaction() === null ? craft()->db->beginTransaction() : null; try { // Fire an 'onBeforeSaveEntry' event $event = new Event($this, array('entry' => $entry, 'isNewEntry' => $isNewEntry)); $this->onBeforeSaveEntry($event); // Is the event giving us the go-ahead? if ($event->performAction) { // Save the element $success = craft()->elements->saveElement($entry); // If it didn't work, rollback the transaction in case something changed in onBeforeSaveEntry if (!$success) { if ($transaction !== null) { $transaction->rollback(); } // If "title" has an error, check if they've defined a custom title label. if ($entry->getError('title')) { // Grab all of the original errors. $errors = $entry->getErrors(); // Grab just the title error message. $originalTitleError = $errors['title']; // Clear the old. $entry->clearErrors(); // Create the new "title" error message. $errors['title'] = str_replace('Title', $entryType->titleLabel, $originalTitleError); // Add all of the errors back on the model. $entry->addErrors($errors); } return false; } // Now that we have an element ID, save it on the other stuff if ($isNewEntry) { $entryRecord->id = $entry->id; } // Save the actual entry row $entryRecord->save(false); if ($section->type == SectionType::Structure) { // Has the parent changed? if ($hasNewParent) { if (!$entry->parentId) { craft()->structures->appendToRoot($section->structureId, $entry); } else { craft()->structures->append($section->structureId, $entry, $parentEntry); } } // Update the entry's descendants, who may be using this entry's URI in their own URIs craft()->elements->updateDescendantSlugsAndUris($entry); } // Save a new version if (craft()->getEdition() >= Craft::Client && $section->enableVersioning) { craft()->entryRevisions->saveVersion($entry); } } else { $success = false; } // Commit the transaction regardless of whether we saved the entry, in case something changed // in onBeforeSaveEntry if ($transaction !== null) { $transaction->commit(); } } catch (\Exception $e) { if ($transaction !== null) { $transaction->rollback(); } throw $e; } if ($success) { // Fire an 'onSaveEntry' event $this->onSaveEntry(new Event($this, array('entry' => $entry, 'isNewEntry' => $isNewEntry))); } return $success; }
/** * Populates the migrations table with the base migration plus any existing ones from app/migrations. * * @throws Exception * @return null */ private function _populateMigrationTable() { $migrations = array(); // Add the base one. $migration = new MigrationRecord(); $migration->version = craft()->migrations->getBaseMigration(); $migration->applyTime = DateTimeHelper::currentUTCDateTime(); $migrations[] = $migration; $migrationsFolder = craft()->path->getAppPath() . 'migrations/'; $migrationFiles = IOHelper::getFolderContents($migrationsFolder, false, "(m(\\d{6}_\\d{6})_.*?)\\.php"); if ($migrationFiles) { foreach ($migrationFiles as $file) { if (IOHelper::fileExists($file)) { $migration = new MigrationRecord(); $migration->version = IOHelper::getFileName($file, false); $migration->applyTime = DateTimeHelper::currentUTCDateTime(); $migrations[] = $migration; } } foreach ($migrations as $migration) { if (!$migration->save()) { Craft::log('Could not populate the migration table.', LogLevel::Error); throw new Exception(Craft::t('There was a problem saving to the migrations table: ') . $this->_getFlattenedErrors($migration->getErrors())); } } } Craft::log('Migration table populated successfully.'); }
/** * @param array $variables */ public function actionSaveRedirect(array $variables = array()) { $this->requirePostRequest(); $redirectId = craft()->request->getPost('redirectId'); if ($redirectId) { $record = craft()->retour->getRedirectById($redirectId); } else { $record = new Retour_StaticRedirectsRecord(); } /* -- Set the record attributes, defaulting to the existing values for whatever is missing from the post data */ $record->locale = craft()->language; $record->redirectMatchType = craft()->request->getPost('redirectMatchType', $record->redirectMatchType); $record->redirectSrcUrl = craft()->request->getPost('redirectSrcUrl', $record->redirectSrcUrl); if ($record->redirectMatchType == "exactmatch" && $record->redirectSrcUrl != "") { $record->redirectSrcUrl = '/' . ltrim($record->redirectSrcUrl, '/'); } $record->redirectSrcUrlParsed = $record->redirectSrcUrl; $record->redirectDestUrl = craft()->request->getPost('redirectDestUrl', $record->redirectDestUrl); $record->redirectHttpCode = craft()->request->getPost('redirectHttpCode', $record->redirectHttpCode); $record->hitLastTime = DateTimeHelper::currentUTCDateTime(); $record->associatedElementId = 0; $result = craft()->retour->saveStaticRedirect($record); if ($result === "" || $result === -1) { $this->redirectToPostedUrl($record); } else { /* -- Send the record back to the template */ craft()->urlManager->setRouteVariables(array('values' => $record)); } }
/** * @static * @param bool $fetch * @return array */ public static function getAlerts($path = null, $fetch = false) { $alerts = array(); $user = craft()->userSession->getUser(); if (!$user) { return $alerts; } if (craft()->updates->isUpdateInfoCached() || $fetch) { // Fetch the updates regardless of whether we're on the Updates page or not, // because the other alerts are relying on cached Elliott info $updateModel = craft()->updates->getUpdates(); if ($path != 'updates' && $user->can('performUpdates')) { if (!empty($updateModel->app->releases)) { if (craft()->updates->criticalCraftUpdateAvailable($updateModel->app->releases)) { $alerts[] = Craft::t('There’s a critical Craft update available.') . ' <a class="go" href="' . UrlHelper::getUrl('updates') . '">' . Craft::t('Go to Updates') . '</a>'; } } } // Domain mismatch? $licenseKeyStatus = craft()->et->getLicenseKeyStatus(); if ($licenseKeyStatus == LicenseKeyStatus::MismatchedDomain) { $licensedDomain = craft()->et->getLicensedDomain(); $licenseKeyPath = craft()->path->getLicenseKeyPath(); $licenseKeyFile = IOHelper::getFolderName($licenseKeyPath, false) . '/' . IOHelper::getFileName($licenseKeyPath); $message = Craft::t('The license located at {file} belongs to {domain}.', array('file' => $licenseKeyFile, 'domain' => '<a href="http://' . $licensedDomain . '" target="_blank">' . $licensedDomain . '</a>')); // Can they actually do something about it? if ($user->admin) { $action = '<a class="domain-mismatch">' . Craft::t('Transfer it to this domain?') . '</a>'; } else { $action = Craft::t('Please notify one of your site’s admins.'); } $alerts[] = $message . ' ' . $action; } // Unlicensed packages? if ($path != 'settings/packages') { $licensedPackages = craft()->et->getLicensedPackages(); $packageTrials = craft()->et->getPackageTrials(); // Could be false if not cached if (is_array($licensedPackages)) { // Look for any unlicensed licenses $unlicensedPackages = array(); foreach (Craft::getPackages() as $package) { if (!in_array($package, $licensedPackages)) { // Make sure it's not in trial if (!is_array($packageTrials) || !in_array($package, array_keys($packageTrials))) { $unlicensedPackages[] = $package; } } } if ($unlicensedPackages) { if (count($unlicensedPackages) == 1) { $message = Craft::t('The {package} package is installed, but it’s not licensed.', array('package' => Craft::t($unlicensedPackages[0]))); } else { $message = Craft::t('You have multiple unlicensed packages installed.'); } // Can they actually do something about it? if ($user->admin) { $action = '<a class="go" href="' . UrlHelper::getUrl('settings/packages') . '">' . Craft::t('Manage packages') . '</a>'; } else { $action = Craft::t('Please notify one of your site’s admins.'); } $alerts[] = $message . ' ' . $action; } } if ($packageTrials && $user->admin && !$user->hasShunned('packageTrialAlert')) { $expiringTrials = array(); $currentTime = DateTimeHelper::currentUTCDateTime(); $nextWeek = $currentTime->add(new DateInterval('P1W'))->getTimestamp(); // See if there are any package trials that expire in less than a week foreach (Craft::getPackages() as $package) { if (!empty($packageTrials[$package])) { if ($packageTrials[$package] < $nextWeek) { $expiringTrials[] = $package; } } } if ($expiringTrials) { if (count($expiringTrials) == 1) { $message = Craft::t('Your {package} trial is expiring soon.', array('package' => Craft::t($expiringTrials[0]))); } else { $message = Craft::t('You have multiple package trials expiring soon.'); } $action1 = '<a class="shun:packageTrialAlert">' . Craft::t('Remind me later') . '</a>'; $action2 = '<a class="go" href="' . UrlHelper::getUrl('settings/packages') . '">' . Craft::t('manage packages') . '</a>'; $alerts[] = $message . ' ' . $action1 . ' ' . Craft::t('or') . ' ' . $action2; } } } } return $alerts; }
/** * Sets a user record up for a new password without saving it. * * @access private * @param UserModel $user * @param UserRecord $userRecord * @return bool */ private function _setPasswordOnUserRecord(UserModel $user, UserRecord $userRecord) { // Validate the password first $passwordModel = new PasswordModel(); $passwordModel->password = $user->newPassword; if ($passwordModel->validate()) { $hashAndType = craft()->security->hashString($user->newPassword); $userRecord->password = $user->password = $hashAndType['hash']; $userRecord->encType = $user->encType = $hashAndType['encType']; $userRecord->status = $user->status = UserStatus::Active; $userRecord->invalidLoginWindowStart = null; $userRecord->invalidLoginCount = $user->invalidLoginCount = null; $userRecord->verificationCode = null; $userRecord->verificationCodeIssuedDate = null; // If it's an existing user, reset the passwordResetRequired bit. if ($user->id) { $userRecord->passwordResetRequired = $user->passwordResetRequired = false; } $userRecord->lastPasswordChangeDate = $user->lastPasswordChangeDate = DateTimeHelper::currentUTCDateTime(); $user->newPassword = null; return true; } else { // If it's a new user AND we allow public registration, set it on the 'password' field and not 'newpassword'. if (!$user->id && craft()->systemSettings->getSetting('users', 'allowPublicRegistration', false)) { $user->addErrors(array('password' => $passwordModel->getErrors('password'))); } else { $user->addErrors(array('newPassword' => $passwordModel->getErrors('password'))); } return false; } }
/** * @return mixed */ public function init() { /* -- Listen for exceptions */ craft()->onException = function (\CExceptionEvent $event) { if ($event->exception instanceof \CHttpException && $event->exception->statusCode == 404) { if (craft()->request->isSiteRequest() && !craft()->request->isLivePreview()) { /* -- See if we should redirect */ $url = urldecode(craft()->request->getRequestUri()); $noQueryUrl = UrlHelper::stripQueryString($url); /* -- Redirect if we find a match, otherwise let Craft handle it */ $redirect = craft()->retour->findRedirectMatch($url); if (isset($redirect)) { craft()->retour->incrementStatistics($url, true); $event->handled = true; RetourPlugin::log("Redirecting " . $url . " to " . $redirect['redirectDestUrl'], LogLevel::Info, false); craft()->request->redirect($redirect['redirectDestUrl'], true, $redirect['redirectHttpCode']); } else { /* -- Now try it without the query string, too, otherwise let Craft handle it */ $redirect = craft()->retour->findRedirectMatch($noQueryUrl); if (isset($redirect)) { craft()->retour->incrementStatistics($url, true); $event->handled = true; RetourPlugin::log("Redirecting " . $url . " to " . $redirect['redirectDestUrl'], LogLevel::Info, false); craft()->request->redirect($redirect['redirectDestUrl'], true, $redirect['redirectHttpCode']); } else { craft()->retour->incrementStatistics($url, false); } } } } }; /* -- Listen for structure changes so we can regenerated our FieldType's URLs */ craft()->on('structures.onMoveElement', function (Event $e) { $element = $e->params['element']; $elemType = $element->getElementType(); if ($element) { if ($elemType == ElementType::Entry) { /* -- Check the field layout, so that we only do this for FieldLayouts that have our Retour fieldtype in them */ $fieldLayouts = $element->fieldLayout->getFields(); foreach ($fieldLayouts as $fieldLayout) { $field = craft()->fields->getFieldById($fieldLayout->fieldId); if ($field->type == "Retour") { craft()->elements->saveElement($element); RetourPlugin::log("Resaved moved structure element", LogLevel::Info, false); break; } } } } }); /* -- Listen for entries whose slug changes */ craft()->on('entries.onBeforeSaveEntry', function (Event $e) { $this->originalUris = array(); if (!$e->params['isNewEntry']) { $entry = $e->params['entry']; $thisSection = $entry->getSection(); if ($thisSection->hasUrls) { $this->originalUris = craft()->retour->getLocalizedUris($entry); } } }); craft()->on('entries.onSaveEntry', function (Event $e) { if (!$e->params['isNewEntry']) { $entry = $e->params['entry']; $newUris = craft()->retour->getLocalizedUris($entry); foreach ($newUris as $newUri) { $oldUri = current($this->originalUris); next($this->originalUris); if (strcmp($oldUri, $newUri) != 0 && $oldUri != "") { $record = new Retour_StaticRedirectsRecord(); /* -- Set the record attributes for our new auto-redirect */ $record->locale = $entry->locale; $record->redirectMatchType = 'exactmatch'; $record->redirectSrcUrl = $oldUri; if ($record->redirectMatchType == "exactmatch" && $record->redirectSrcUrl != "") { $record->redirectSrcUrl = '/' . ltrim($record->redirectSrcUrl, '/'); } $record->redirectSrcUrlParsed = $record->redirectSrcUrl; $record->redirectDestUrl = $newUri; if ($record->redirectMatchType == "exactmatch" && $record->redirectDestUrl != "") { $record->redirectDestUrl = '/' . ltrim($record->redirectDestUrl, '/'); } $record->redirectHttpCode = '301'; $record->hitLastTime = DateTimeHelper::currentUTCDateTime(); $record->associatedElementId = 0; $result = craft()->retour->saveStaticRedirect($record); } } } }); }
/** * Returns whether an entry's date field is empty. * * @param EntryModel $entry * @param string $fieldHandle * * @return bool */ protected function fieldIsEmpty($entry, $fieldHandle) { if ($fieldHandle == 'postDate') { return $entry->{$fieldHandle} == DateTimeHelper::currentUTCDateTime(); } return $entry->{$fieldHandle} === null; }
private function _storeFile(EmbeddedAssetsModel $media, $folderId) { $fileLabel = substr(preg_replace('/[^a-z0-9]+/i', '-', $media->getTitle()), 0, 40); $filePrefix = EmbeddedAssetsPlugin::getFileNamePrefix(); $fileExtension = '.json'; $fileName = $filePrefix . $fileLabel . $fileExtension; $existingFile = craft()->assets->findFile(array('folderId' => $folderId, 'filename' => $fileName)); if ($existingFile) { EmbeddedAssetsPlugin::log("File with name \"{$fileName}\" already exists in this location"); $fileUniqueId = DateTimeHelper::currentUTCDateTime()->format('ymd_His'); $fileName = $filePrefix . $fileLabel . '_' . $fileUniqueId . $fileExtension; } $fileData = $media->getAttributes(null, true); $fileData['__embeddedasset__'] = true; unset($fileData['id']); unset($fileData['settings']); $this->_addToFiles('assets-upload', $fileName, JsonHelper::encode($fileData), 'application/json'); $response = craft()->assets->uploadFile($folderId); if ($response->isSuccess()) { $fileId = $response->getDataItem('fileId'); $file = craft()->assets->getFileById($fileId); return $file; } else { throw new \Exception($response->errorMessage); } }
/** * @param $url The 404 url */ public function incrementStatistics($url, $handled = false) { $handled = (int) $handled; $url = substr($url, 0, 255); $referrer = craft()->request->getUrlReferrer(); if (is_null($referrer)) { $referrer = ""; } /* -- See if a stats record exists already */ $result = craft()->db->createCommand()->select('*')->from('retour_stats')->where('redirectSrcUrl =' . craft()->db->quoteValue($url))->queryAll(); if (empty($result)) { $stats = new Retour_StatsRecord(); $stats->redirectSrcUrl = $url; $stats->referrerUrl = $referrer; $stats->hitCount = 1; $stats->hitLastTime = DateTimeHelper::currentUTCDateTime(); $stats->handledByRetour = $handled; $stats->save(); } else { /* -- Update the stats table */ foreach ($result as $stat) { $stat['hitCount'] = $stat['hitCount'] + 1; $stat['hitLastTime'] = DateTimeHelper::currentTimeForDb(); $stat['referrerUrl'] = $referrer; $result = craft()->db->createCommand()->update('retour_stats', array('hitCount' => $stat['hitCount'], 'hitLastTime' => $stat['hitLastTime'], 'handledByRetour' => $handled, 'referrerUrl' => $stat['referrerUrl']), 'id=:id', array(':id' => $stat['id'])); } } }
/** * Sets a user record up for a new password without saving it. * * @param UserModel $user The user who is getting a new password. * @param UserRecord $userRecord The user’s record. * @param bool $updatePasswordResetRequired Whether the user’s * {@link UserModel::passwordResetRequired passwordResetRequired} * attribute should be set `false`. Default is `true`. * @param bool $forceDifferentPassword Whether to force a new password to be different from any existing * password. * * @return bool */ private function _setPasswordOnUserRecord(UserModel $user, UserRecord $userRecord, $updatePasswordResetRequired = true, $forceDifferentPassword = false) { // Validate the password first $passwordModel = new PasswordModel(); $passwordModel->password = $user->newPassword; $validates = false; // If it's a new user AND we allow public registration, set it on the 'password' field and not 'newpassword'. if (!$user->id && craft()->systemSettings->getSetting('users', 'allowPublicRegistration')) { $passwordErrorField = 'password'; } else { $passwordErrorField = 'newPassword'; } if ($passwordModel->validate()) { if ($forceDifferentPassword) { // See if the passwords are the same. if (craft()->security->checkPassword($user->newPassword, $userRecord->password)) { $user->addErrors(array($passwordErrorField => Craft::t('That password is the same as your old password. Please choose a new one.'))); } else { $validates = true; } } else { $validates = true; } if ($validates) { // Fire an 'onBeforeSetPassword' event $event = new Event($this, array('password' => $user->newPassword, 'user' => $user)); $this->onBeforeSetPassword($event); // Is the event is giving us the go-ahead? $validates = $event->performAction; } } if ($validates) { $hash = craft()->security->hashPassword($user->newPassword); $userRecord->password = $user->password = $hash; $userRecord->invalidLoginWindowStart = null; $userRecord->invalidLoginCount = $user->invalidLoginCount = null; $userRecord->verificationCode = null; $userRecord->verificationCodeIssuedDate = null; // If it's an existing user, reset the passwordResetRequired bit. if ($updatePasswordResetRequired && $user->id) { $userRecord->passwordResetRequired = $user->passwordResetRequired = false; } $userRecord->lastPasswordChangeDate = $user->lastPasswordChangeDate = DateTimeHelper::currentUTCDateTime(); $user->newPassword = null; $success = true; } else { $user->addErrors(array($passwordErrorField => $passwordModel->getErrors('password'))); $success = false; } if ($success) { // Fire an 'onSetPassword' event $this->onSetPassword(new Event($this, array('user' => $user))); } return $success; }
/** * Return a filename replacement for a filename in a list of files. The file * list typically represents a folder's contents. * * @param array $fileList * @param $originalFilename * * @throws Exception When a replacement cannot be found. * @return string $filename */ public static function getFilenameReplacement(array $fileList, $originalFilename) { foreach ($fileList as &$file) { $file = StringHelper::toLowerCase($file); } $fileList = array_flip($fileList); // Shorthand. $canUse = function ($filenameToTest) use($fileList) { return !isset($fileList[StringHelper::toLowerCase($filenameToTest)]); }; if ($canUse($originalFilename)) { return $originalFilename; } $extension = IOHelper::getExtension($originalFilename); $filename = IOHelper::getFileName($originalFilename, false); // If the file already ends with something that looks like a timestamp, use that instead. if (preg_match('/.*_([0-9]{6}_[0-9]{6})$/', $filename, $matches)) { $base = $filename; } else { $timestamp = DateTimeHelper::currentUTCDateTime()->format("ymd_His"); $base = $filename . '_' . $timestamp; } $newFilename = $base . '.' . $extension; if ($canUse($newFilename)) { return $newFilename; } $increment = 0; while (++$increment) { $newFilename = $base . '_' . $increment . '.' . $extension; if ($canUse($newFilename)) { break; } if ($increment == 50) { throw new Exception(Craft::t("A suitable replacement name cannot be found for “{filename}”", array('filename' => $originalFilename))); } } return $newFilename; }
/** * @return null */ private function _cleanStaleSessions() { $interval = new DateInterval('P3M'); $expire = DateTimeHelper::currentUTCDateTime(); $pastTimeStamp = $expire->sub($interval)->getTimestamp(); $pastTime = DateTimeHelper::formatTimeForDb($pastTimeStamp); craft()->db->createCommand()->delete('sessions', 'dateUpdated < :pastTime', array('pastTime' => $pastTime)); }
/** * Populates the migrations table with the base migration. * * @throws Exception */ private function _populateMigrationTable() { $migration = new MigrationRecord(); $migration->version = craft()->migrations->getBaseMigration(); $migration->applyTime = DateTimeHelper::currentUTCDateTime(); if ($migration->save()) { Craft::log('Migration table populated successfully.'); } else { Craft::log('Could not populate the migration table.', LogLevel::Error); throw new Exception(Craft::t('There was a problem saving to the migrations table:') . $this->_getFlattenedErrors($migration->getErrors())); } }
/** * If the plugin already had a migrations folder with migrations in it, let's save them in the db. * * @param int $pluginId * @param string $pluginHandle * * @throws Exception */ private function _savePluginMigrations($pluginId, $pluginHandle) { $migrationsFolder = craft()->path->getPluginsPath() . mb_strtolower($pluginHandle) . '/migrations/'; if (IOHelper::folderExists($migrationsFolder)) { $migrations = array(); $migrationFiles = IOHelper::getFolderContents($migrationsFolder, false, "(m(\\d{6}_\\d{6})_.*?)\\.php"); if ($migrationFiles) { foreach ($migrationFiles as $file) { if (IOHelper::fileExists($file)) { $migration = new MigrationRecord(); $migration->version = IOHelper::getFileName($file, false); $migration->applyTime = DateTimeHelper::currentUTCDateTime(); $migration->pluginId = $pluginId; $migrations[] = $migration; } } foreach ($migrations as $migration) { if (!$migration->save()) { throw new Exception(Craft::t('There was a problem saving to the migrations table: ') . $this->_getFlattenedErrors($migration->getErrors())); } } } } }
/** * @throws EtException|\Exception * @return EtModel|null */ public function phoneHome() { try { $missingLicenseKey = empty($this->_model->licenseKey); // No craft/config/license.key file and we can't even write to the config folder. Don't even make the call home. if ($missingLicenseKey && !$this->_isConfigFolderWritable()) { throw new EtException('Craft needs to be able to write to your “craft/config” folder and it can’t.', 10001); } if (!craft()->fileCache->get('etConnectFailure')) { $data = JsonHelper::encode($this->_model->getAttributes(null, true)); $response = \Requests::post($this->_endpoint, array(), $data, $this->_options); if ($response->success) { // Clear the connection failure cached item if it exists. if (craft()->fileCache->get('etConnectFailure')) { craft()->fileCache->delete('etConnectFailure'); } if (isset($this->_options['filename'])) { $fileName = IOHelper::getFileName($this->_options['filename'], false); // If the file name is a UUID, we know it was temporarily set and they want to use the name of the file that was on the sending server. if (StringHelper::isUUID($fileName)) { $contentDisposition = $response->headers->offsetGet('content-disposition'); preg_match("/\"(.*)\"/us", $contentDisposition, $matches); $fileName = $matches[1]; IOHelper::rename($this->_options['filename'], IOHelper::getFolderName($this->_options['filename']) . $fileName); } return $fileName; } $etModel = craft()->et->decodeEtModel($response->body); if ($etModel) { if ($missingLicenseKey && !empty($etModel->licenseKey)) { $this->_setLicenseKey($etModel->licenseKey); } // Do some packageTrial timestamp to datetime conversions. if (!empty($etModel->packageTrials)) { $packageTrials = $etModel->packageTrials; foreach ($etModel->packageTrials as $packageHandle => $expiryTimestamp) { $expiryDate = DateTime::createFromFormat('U', $expiryTimestamp); $currentDate = DateTimeHelper::currentUTCDateTime(); if ($currentDate > $expiryDate) { unset($packageTrials[$packageHandle]); } } $etModel->packageTrials = $packageTrials; } // Cache the license key status and which packages are associated with it craft()->fileCache->set('licenseKeyStatus', $etModel->licenseKeyStatus); craft()->fileCache->set('licensedPackages', $etModel->licensedPackages); craft()->fileCache->set('packageTrials', $etModel->packageTrials); if ($etModel->licenseKeyStatus == LicenseKeyStatus::MismatchedDomain) { craft()->fileCache->set('licensedDomain', $etModel->licensedDomain); } return $etModel; } else { Craft::log('Error in calling ' . $this->_endpoint . ' Response: ' . $response->body, LogLevel::Warning); if (craft()->fileCache->get('etConnectFailure')) { // There was an error, but at least we connected. craft()->fileCache->delete('etConnectFailure'); } } } else { Craft::log('Error in calling ' . $this->_endpoint . ' Response: ' . $response->body, LogLevel::Warning); if (craft()->fileCache->get('etConnectFailure')) { // There was an error, but at least we connected. craft()->fileCache->delete('etConnectFailure'); } } } } catch (EtException $e) { Craft::log('Error in ' . __METHOD__ . '. Message: ' . $e->getMessage(), LogLevel::Error); if (craft()->fileCache->get('etConnectFailure')) { // There was an error, but at least we connected. craft()->fileCache->delete('etConnectFailure'); } throw $e; } catch (\Exception $e) { Craft::log('Error in ' . __METHOD__ . '. Message: ' . $e->getMessage(), LogLevel::Error); // Cache the failure for 5 minutes so we don't try again. craft()->fileCache->set('etConnectFailure', true, 300); } return null; }