/** * Saves a category group. * * @param CategoryGroupModel $group * * @throws \Exception * @return bool */ public function saveGroup(CategoryGroupModel $group) { if ($group->id) { $groupRecord = CategoryGroupRecord::model()->findById($group->id); if (!$groupRecord) { throw new Exception(Craft::t('No category group exists with the ID “{id}”.', array('id' => $group->id))); } $oldCategoryGroup = CategoryGroupModel::populateModel($groupRecord); $isNewCategoryGroup = false; } else { $groupRecord = new CategoryGroupRecord(); $isNewCategoryGroup = true; } // If they've set maxLevels to 0 (don't ask why), then pretend like there are none. if ($group->maxLevels == 0) { $group->maxLevels = null; } $groupRecord->name = $group->name; $groupRecord->handle = $group->handle; $groupRecord->hasUrls = $group->hasUrls; if ($group->hasUrls) { $groupRecord->template = $group->template; } else { $groupRecord->template = $group->template = null; } // Make sure that all of the URL formats are set properly $groupLocales = $group->getLocales(); foreach ($groupLocales as $localeId => $groupLocale) { if ($group->hasUrls) { $urlFormatAttributes = array('urlFormat'); $groupLocale->urlFormatIsRequired = true; if ($group->maxLevels == 1) { $groupLocale->nestedUrlFormat = null; } else { $urlFormatAttributes[] = 'nestedUrlFormat'; $groupLocale->nestedUrlFormatIsRequired = true; } foreach ($urlFormatAttributes as $attribute) { if (!$groupLocale->validate(array($attribute))) { $group->addError($attribute . '-' . $localeId, $groupLocale->getError($attribute)); } } } else { $groupLocale->urlFormat = null; $groupLocale->nestedUrlFormat = null; } } // Validate! $groupRecord->validate(); $group->addErrors($groupRecord->getErrors()); if (!$group->hasErrors()) { $transaction = craft()->db->getCurrentTransaction() === null ? craft()->db->beginTransaction() : null; try { // Create/update the structure if ($isNewCategoryGroup) { $structure = new StructureModel(); } else { $structure = craft()->structures->getStructureById($oldCategoryGroup->structureId); } $structure->maxLevels = $group->maxLevels; craft()->structures->saveStructure($structure); $groupRecord->structureId = $structure->id; $group->structureId = $structure->id; // Is there a new field layout? $fieldLayout = $group->getFieldLayout(); if (!$fieldLayout->id) { // Delete the old one if (!$isNewCategoryGroup && $oldCategoryGroup->fieldLayoutId) { craft()->fields->deleteLayoutById($oldCategoryGroup->fieldLayoutId); } // Save the new one craft()->fields->saveLayout($fieldLayout); // Update the category group record/model with the new layout ID $groupRecord->fieldLayoutId = $fieldLayout->id; $group->fieldLayoutId = $fieldLayout->id; } // Save the category group $groupRecord->save(false); // Now that we have a category group ID, save it on the model if (!$group->id) { $group->id = $groupRecord->id; } // Might as well update our cache of the category group while we have it. $this->_categoryGroupsById[$group->id] = $group; // Update the categorygroups_i18n table $newLocaleData = array(); if (!$isNewCategoryGroup) { // Get the old category group locales $oldLocaleRecords = CategoryGroupLocaleRecord::model()->findAllByAttributes(array('groupId' => $group->id)); $oldLocales = CategoryGroupLocaleModel::populateModels($oldLocaleRecords, 'locale'); $changedLocaleIds = array(); } foreach ($groupLocales as $localeId => $locale) { // Was this already selected? if (!$isNewCategoryGroup && isset($oldLocales[$localeId])) { $oldLocale = $oldLocales[$localeId]; // Has the URL format changed? if ($locale->urlFormat != $oldLocale->urlFormat || $locale->nestedUrlFormat != $oldLocale->nestedUrlFormat) { craft()->db->createCommand()->update('categorygroups_i18n', array('urlFormat' => $locale->urlFormat, 'nestedUrlFormat' => $locale->nestedUrlFormat), array('id' => $oldLocale->id)); $changedLocaleIds[] = $localeId; } } else { $newLocaleData[] = array($group->id, $localeId, $locale->urlFormat, $locale->nestedUrlFormat); } } // Insert the new locales craft()->db->createCommand()->insertAll('categorygroups_i18n', array('groupId', 'locale', 'urlFormat', 'nestedUrlFormat'), $newLocaleData); if (!$isNewCategoryGroup) { // Drop any locales that are no longer being used, as well as the associated category/element // locale rows $droppedLocaleIds = array_diff(array_keys($oldLocales), array_keys($groupLocales)); if ($droppedLocaleIds) { craft()->db->createCommand()->delete('categorygroups_i18n', array('in', 'locale', $droppedLocaleIds)); } } // Finally, deal with the existing categories... if (!$isNewCategoryGroup) { // Get all of the category IDs in this group $criteria = craft()->elements->getCriteria(ElementType::Category); $criteria->groupId = $group->id; $criteria->status = null; $criteria->limit = null; $categoryIds = $criteria->ids(); // Should we be deleting if ($categoryIds && $droppedLocaleIds) { craft()->db->createCommand()->delete('elements_i18n', array('and', array('in', 'elementId', $categoryIds), array('in', 'locale', $droppedLocaleIds))); craft()->db->createCommand()->delete('content', array('and', array('in', 'elementId', $categoryIds), array('in', 'locale', $droppedLocaleIds))); } // Are there any locales left? if ($groupLocales) { // Drop the old category URIs if the group no longer has URLs if (!$group->hasUrls && $oldCategoryGroup->hasUrls) { craft()->db->createCommand()->update('elements_i18n', array('uri' => null), array('in', 'elementId', $categoryIds)); } else { if ($changedLocaleIds) { foreach ($categoryIds as $categoryId) { craft()->config->maxPowerCaptain(); // Loop through each of the changed locales and update all of the categories’ slugs and // URIs foreach ($changedLocaleIds as $localeId) { $criteria = craft()->elements->getCriteria(ElementType::Category); $criteria->id = $categoryId; $criteria->locale = $localeId; $criteria->status = null; $category = $criteria->first(); // todo: replace the getContent()->id check with 'strictLocale' param once it's added if ($category && $category->getContent()->id) { craft()->elements->updateElementSlugAndUri($category, false, false); } } } } } } } if ($transaction !== null) { $transaction->commit(); } } catch (\Exception $e) { if ($transaction !== null) { $transaction->rollback(); } throw $e; } return true; } else { return false; } }