flipbox /
meta
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * @copyright Copyright (c) Flipbox Digital Limited |
||
| 5 | * @license https://flipboxfactory.com/software/meta/license |
||
| 6 | * @link https://www.flipboxfactory.com/software/meta/ |
||
| 7 | */ |
||
| 8 | |||
| 9 | namespace flipbox\meta\services; |
||
| 10 | |||
| 11 | use Craft; |
||
| 12 | use craft\base\Element; |
||
| 13 | use craft\base\ElementInterface; |
||
| 14 | use craft\db\Query; |
||
| 15 | use craft\helpers\StringHelper; |
||
| 16 | use flipbox\meta\elements\db\Meta as MetaQuery; |
||
| 17 | use flipbox\meta\elements\Meta as MetaElement; |
||
| 18 | use flipbox\meta\fields\Meta as MetaField; |
||
| 19 | use flipbox\meta\helpers\Field as FieldHelper; |
||
| 20 | use yii\base\Component; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * @author Flipbox Factory <[email protected]> |
||
| 24 | * @since 1.0.0 |
||
| 25 | * |
||
| 26 | * @method MetaQuery find() |
||
| 27 | */ |
||
| 28 | class Field extends Component |
||
| 29 | { |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @param MetaField $field |
||
| 33 | * @param ElementInterface $element |
||
| 34 | * @return bool |
||
| 35 | */ |
||
| 36 | public function beforeElementDelete(MetaField $field, ElementInterface $element): bool |
||
| 37 | { |
||
| 38 | |||
| 39 | // Delete any meta elements that belong to this element(s) |
||
| 40 | foreach (Craft::$app->getSites()->getAllSiteIds() as $siteId) { |
||
| 41 | $query = MetaElement::find(); |
||
| 42 | $query->status(null); |
||
| 43 | $query->enabledForSite(false); |
||
| 44 | $query->fieldId($field->id); |
||
| 45 | $query->siteId($siteId); |
||
| 46 | $query->owner($element); |
||
| 47 | |||
| 48 | /** @var MetaElement $meta */ |
||
| 49 | foreach ($query->all() as $meta) { |
||
| 50 | Craft::$app->getElements()->deleteElement($meta); |
||
| 51 | } |
||
| 52 | } |
||
| 53 | |||
| 54 | return true; |
||
| 55 | } |
||
| 56 | |||
| 57 | /** |
||
| 58 | * @param MetaField $field |
||
| 59 | * @param ElementInterface $owner |
||
| 60 | * @return bool |
||
| 61 | * @throws \Exception |
||
| 62 | * @throws \yii\db\Exception |
||
| 63 | */ |
||
| 64 | public function afterElementSave(MetaField $field, ElementInterface $owner) |
||
| 65 | { |
||
| 66 | |||
| 67 | /** @var Element $owner */ |
||
| 68 | |||
| 69 | /** @var MetaQuery $query */ |
||
| 70 | $query = $owner->getFieldValue($field->handle); |
||
| 71 | |||
| 72 | $transaction = Craft::$app->getDb()->beginTransaction(); |
||
| 73 | try { |
||
| 74 | // Do localization |
||
| 75 | $this->applyFieldLocalizationSetting($owner, $field, $query); |
||
| 76 | |||
| 77 | $metaIds = []; |
||
| 78 | |||
| 79 | /** @var Meta $element */ |
||
| 80 | foreach ($query->all() as $meta) { |
||
| 81 | // Set owner based attributes |
||
| 82 | $meta->ownerId = $owner->id; |
||
| 83 | $meta->ownerSiteId = ($field->localize ? $owner->siteId : null); |
||
| 84 | |||
| 85 | // Save |
||
| 86 | Craft::$app->getElements()->saveElement($meta, false); |
||
| 87 | |||
| 88 | $metaIds[] = $meta->id; |
||
| 89 | } |
||
| 90 | |||
| 91 | // Delete any elements that shouldn't be there anymore |
||
| 92 | $deleteElementsQuery = MetaElement::find() |
||
| 93 | ->ownerId($owner->getId()) |
||
| 94 | ->fieldId($field->id) |
||
| 95 | ->andWhere(['not', ['elements.id' => $metaIds]]); |
||
| 96 | |||
| 97 | if ($field->localize) { |
||
| 98 | $deleteElementsQuery->ownerSiteId = $owner->siteId; |
||
| 99 | } else { |
||
| 100 | $deleteElementsQuery->siteId = $owner->siteId; |
||
| 101 | } |
||
| 102 | |||
| 103 | foreach ($deleteElementsQuery->all() as $deleteElement) { |
||
| 104 | Craft::$app->getElements()->deleteElement($deleteElement); |
||
| 105 | } |
||
| 106 | |||
| 107 | // Success |
||
| 108 | $transaction->commit(); |
||
| 109 | } catch (\Exception $e) { |
||
| 110 | // Revert |
||
| 111 | $transaction->rollback(); |
||
| 112 | |||
| 113 | throw $e; |
||
| 114 | } |
||
| 115 | |||
| 116 | return true; |
||
| 117 | } |
||
| 118 | |||
| 119 | /** |
||
| 120 | * Applies the field's translation setting to a set of elements. |
||
| 121 | * |
||
| 122 | * @param ElementInterface $owner |
||
| 123 | * @param MetaField $field |
||
| 124 | * @param MetaQuery $query |
||
| 125 | * |
||
| 126 | * @return void |
||
| 127 | */ |
||
| 128 | private function applyFieldLocalizationSetting(ElementInterface $owner, MetaField $field, MetaQuery $query) |
||
| 129 | { |
||
| 130 | |||
| 131 | /** @var Element $owner */ |
||
| 132 | |||
| 133 | // Does it look like any work is needed here? |
||
| 134 | $applyNewLocalizationSetting = false; |
||
| 135 | |||
| 136 | foreach ($query->all() as $meta) { |
||
| 137 | if ($meta->id && ( |
||
| 138 | ($field->localize && !$meta->ownerSiteId) || |
||
| 139 | (!$field->localize && $meta->ownerSiteId) |
||
| 140 | ) |
||
| 141 | ) { |
||
| 142 | $applyNewLocalizationSetting = true; |
||
| 143 | break; |
||
| 144 | } |
||
| 145 | } |
||
| 146 | |||
| 147 | if (!$applyNewLocalizationSetting) { |
||
| 148 | // All good |
||
| 149 | return; |
||
| 150 | } |
||
| 151 | |||
| 152 | // Get all of the elements for this field/owner that use the other locales, whose ownerLocale attribute is set |
||
| 153 | // incorrectly |
||
| 154 | $elementsInOtherSites = []; |
||
| 155 | |||
| 156 | $query = MetaElement::find() |
||
| 157 | ->fieldId($field->id) |
||
| 158 | ->ownerId($owner->getId()) |
||
| 159 | ->status(null) |
||
| 160 | ->enabledForSite(false) |
||
| 161 | ->limit(null); |
||
| 162 | |||
| 163 | if ($field->localize) { |
||
| 164 | $query->ownerSiteId(':empty:'); |
||
|
0 ignored issues
–
show
|
|||
| 165 | } |
||
| 166 | |||
| 167 | foreach (Craft::$app->getI18n()->getSiteLocaleIds() as $siteId) { |
||
| 168 | if ($siteId === $owner->siteId) { |
||
| 169 | continue; |
||
| 170 | } |
||
| 171 | |||
| 172 | $query->siteId($siteId); |
||
| 173 | |||
| 174 | if (!$field->localize) { |
||
| 175 | $query->ownerSiteId($siteId); |
||
|
0 ignored issues
–
show
The method
ownerSiteId() does not exist on craft\elements\db\ElementQuery. Did you maybe mean siteId()?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. Loading history...
|
|||
| 176 | } |
||
| 177 | |||
| 178 | $elementsInOtherSite = $query->all(); |
||
| 179 | |||
| 180 | if (!empty($elementsInOtherSite)) { |
||
| 181 | $elementsInOtherSites[$siteId] = $elementsInOtherSite; |
||
| 182 | } |
||
| 183 | } |
||
| 184 | |||
| 185 | if (empty($elementsInOtherSites)) { |
||
| 186 | return; |
||
| 187 | } |
||
| 188 | |||
| 189 | if ($field->localize) { |
||
| 190 | $newElementIds = []; |
||
| 191 | |||
| 192 | // Duplicate the other-site elements so each site has their own unique set of elements |
||
| 193 | foreach ($elementsInOtherSites as $siteId => $elementsInOtherSite) { |
||
| 194 | foreach ($elementsInOtherSite as $elementInOtherSite) { |
||
| 195 | /** @var MetaElement $elementInOtherSite */ |
||
| 196 | $originalElementId = $elementInOtherSite->id; |
||
| 197 | |||
| 198 | $elementInOtherSite->id = null; |
||
| 199 | $elementInOtherSite->contentId = null; |
||
| 200 | $elementInOtherSite->ownerSiteId = (int)$siteId; |
||
| 201 | Craft::$app->getElements()->saveElement($elementInOtherSite, false); |
||
| 202 | |||
| 203 | $newElementIds[$originalElementId][$siteId] = $elementInOtherSite->id; |
||
| 204 | } |
||
| 205 | } |
||
| 206 | |||
| 207 | // Duplicate the relations, too. First by getting all of the existing relations for the original |
||
| 208 | // elements |
||
| 209 | $relations = (new Query()) |
||
| 210 | ->select([ |
||
| 211 | 'fieldId', |
||
| 212 | 'sourceId', |
||
| 213 | 'sourceSiteId', |
||
| 214 | 'targetId', |
||
| 215 | 'sortOrder' |
||
| 216 | ]) |
||
| 217 | ->from(['{{%relations}}']) |
||
| 218 | ->where(['sourceId' => array_keys($newElementIds)]) |
||
| 219 | ->all(); |
||
| 220 | |||
| 221 | if (!empty($relations)) { |
||
| 222 | // Now duplicate each one for the other sites' new elements |
||
| 223 | $rows = []; |
||
| 224 | |||
| 225 | foreach ($relations as $relation) { |
||
| 226 | $originalElementId = $relation['sourceId']; |
||
| 227 | |||
| 228 | // Just to be safe... |
||
| 229 | if (isset($newElementIds[$originalElementId])) { |
||
| 230 | foreach ($newElementIds[$originalElementId] as $siteId => $newElementId) { |
||
| 231 | $rows[] = [ |
||
| 232 | $relation['fieldId'], |
||
| 233 | $newElementId, |
||
| 234 | $relation['sourceSiteId'], |
||
| 235 | $relation['targetId'], |
||
| 236 | $relation['sortOrder'] |
||
| 237 | ]; |
||
| 238 | } |
||
| 239 | } |
||
| 240 | } |
||
| 241 | |||
| 242 | Craft::$app->getDb()->createCommand() |
||
| 243 | ->batchInsert( |
||
| 244 | 'relations', |
||
| 245 | [ |
||
| 246 | 'fieldId', |
||
| 247 | 'sourceId', |
||
| 248 | 'sourceSiteId', |
||
| 249 | 'targetId', |
||
| 250 | 'sortOrder' |
||
| 251 | ], |
||
| 252 | $rows |
||
| 253 | ) |
||
| 254 | ->execute(); |
||
| 255 | } |
||
| 256 | } else { |
||
| 257 | // Delete all of these elements |
||
| 258 | $deletedElementIds = []; |
||
| 259 | |||
| 260 | foreach ($elementsInOtherSites as $elementsInOtherSite) { |
||
| 261 | foreach ($elementsInOtherSite as $elementInOtherSite) { |
||
| 262 | // Have we already deleted this element? |
||
| 263 | if (in_array($elementInOtherSite->id, $deletedElementIds, false)) { |
||
| 264 | continue; |
||
| 265 | } |
||
| 266 | |||
| 267 | Craft::$app->getElements()->deleteElement($elementInOtherSite); |
||
| 268 | $deletedElementIds[] = $elementInOtherSite->id; |
||
| 269 | } |
||
| 270 | } |
||
| 271 | } |
||
| 272 | } |
||
| 273 | |||
| 274 | /** |
||
| 275 | * Returns the content table name for a given Meta field. |
||
| 276 | * |
||
| 277 | * @param MetaField $metaField |
||
| 278 | * @return string |
||
| 279 | */ |
||
| 280 | public function getContentTableName(MetaField $metaField): string |
||
| 281 | { |
||
| 282 | return FieldHelper::getContentTableName($metaField->id); |
||
| 283 | } |
||
| 284 | } |
||
| 285 |
This check marks calls to methods that do not seem to exist on an object.
This is most likely the result of a method being renamed without all references to it being renamed likewise.