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 | namespace SilverStripe\BehatExtension\Context; |
||
4 | |||
5 | use Behat\Behat\Context\BehatContext; |
||
6 | use Behat\Behat\Event\ScenarioEvent; |
||
7 | use Behat\Gherkin\Node\PyStringNode; |
||
8 | use Behat\Gherkin\Node\TableNode; |
||
9 | use SilverStripe\Filesystem\Storage\AssetStore; |
||
10 | use SilverStripe\ORM\DB; |
||
11 | use SilverStripe\ORM\DataObject; |
||
12 | use SilverStripe\ORM\Versioning\Versioned; |
||
13 | use SilverStripe\Security\Permission; |
||
14 | |||
15 | // PHPUnit |
||
16 | require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php'; |
||
17 | |||
18 | /** |
||
19 | * Context used to create fixtures in the SilverStripe ORM. |
||
20 | */ |
||
21 | class FixtureContext extends BehatContext |
||
22 | { |
||
23 | protected $context; |
||
24 | |||
25 | /** |
||
26 | * @var \FixtureFactory |
||
27 | */ |
||
28 | protected $fixtureFactory; |
||
29 | |||
30 | /** |
||
31 | * @var String Absolute path where file fixtures are located. |
||
32 | * These will automatically get copied to their location |
||
33 | * declare through the 'Given a file "..."' step defition. |
||
34 | */ |
||
35 | protected $filesPath; |
||
36 | |||
37 | /** |
||
38 | * @var String Tracks all files and folders created from fixtures, for later cleanup. |
||
39 | */ |
||
40 | protected $createdFilesPaths = array(); |
||
41 | |||
42 | /** |
||
43 | * @var array Stores the asset tuples. |
||
44 | */ |
||
45 | protected $createdAssets = array(); |
||
46 | |||
47 | public function __construct(array $parameters) |
||
48 | { |
||
49 | $this->context = $parameters; |
||
50 | } |
||
51 | |||
52 | public function getSession($name = null) |
||
53 | { |
||
54 | return $this->getMainContext()->getSession($name); |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * @return \FixtureFactory |
||
59 | */ |
||
60 | public function getFixtureFactory() |
||
61 | { |
||
62 | if (!$this->fixtureFactory) { |
||
63 | $this->fixtureFactory = \Injector::inst()->create('FixtureFactory', 'FixtureContextFactory'); |
||
64 | } |
||
65 | return $this->fixtureFactory; |
||
66 | } |
||
67 | |||
68 | /** |
||
69 | * @param \FixtureFactory $factory |
||
70 | */ |
||
71 | public function setFixtureFactory(\FixtureFactory $factory) |
||
72 | { |
||
73 | $this->fixtureFactory = $factory; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * @param String |
||
78 | */ |
||
79 | public function setFilesPath($path) |
||
80 | { |
||
81 | $this->filesPath = $path; |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * @return String |
||
86 | */ |
||
87 | public function getFilesPath() |
||
88 | { |
||
89 | return $this->filesPath; |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * @BeforeScenario @database-defaults |
||
94 | */ |
||
95 | public function beforeDatabaseDefaults(ScenarioEvent $event) |
||
96 | { |
||
97 | \SapphireTest::empty_temp_db(); |
||
98 | DB::get_conn()->quiet(); |
||
99 | $dataClasses = \ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject'); |
||
100 | array_shift($dataClasses); |
||
101 | foreach ($dataClasses as $dataClass) { |
||
102 | \singleton($dataClass)->requireDefaultRecords(); |
||
103 | } |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * @AfterScenario |
||
108 | */ |
||
109 | public function afterResetDatabase(ScenarioEvent $event) |
||
110 | { |
||
111 | \SapphireTest::empty_temp_db(); |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * @AfterScenario |
||
116 | */ |
||
117 | public function afterResetAssets(ScenarioEvent $event) |
||
118 | { |
||
119 | $store = $this->getAssetStore(); |
||
120 | if (is_array($this->createdAssets)) { |
||
121 | foreach ($this->createdAssets as $asset) { |
||
122 | $store->delete($asset['FileFilename'], $asset['FileHash']); |
||
123 | } |
||
124 | } |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Example: Given a "page" "Page 1" |
||
129 | * |
||
130 | * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)"$/ |
||
131 | */ |
||
132 | public function stepCreateRecord($type, $id) |
||
133 | { |
||
134 | $class = $this->convertTypeToClass($type); |
||
135 | $fields = $this->prepareFixture($class, $id); |
||
136 | $this->fixtureFactory->createObject($class, $id, $fields); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Example: Given a "page" "Page 1" has the "content" "My content" |
||
141 | * |
||
142 | * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" has (?:(an|a|the) )"(?<field>.*)" "(?<value>.*)"$/ |
||
143 | */ |
||
144 | public function stepCreateRecordHasField($type, $id, $field, $value) |
||
145 | { |
||
146 | $class = $this->convertTypeToClass($type); |
||
147 | $fields = $this->convertFields( |
||
148 | $class, |
||
149 | array($field => $value) |
||
150 | ); |
||
151 | // We should check if this fixture object already exists - if it does, we update it. If not, we create it |
||
152 | View Code Duplication | if ($existingFixture = $this->fixtureFactory->get($class, $id)) { |
|
153 | // Merge existing data with new data, and create new object to replace existing object |
||
154 | foreach ($fields as $k => $v) { |
||
155 | $existingFixture->$k = $v; |
||
156 | } |
||
157 | $existingFixture->write(); |
||
158 | } else { |
||
159 | $this->fixtureFactory->createObject($class, $id, $fields); |
||
160 | } |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Example: Given a "page" "Page 1" with "URL"="page-1" and "Content"="my page 1" |
||
165 | * Example: Given the "page" "Page 1" has "URL"="page-1" and "Content"="my page 1" |
||
166 | * |
||
167 | * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (?:(with|has)) (?<data>".*)$/ |
||
168 | */ |
||
169 | public function stepCreateRecordWithData($type, $id, $data) |
||
170 | { |
||
171 | $class = $this->convertTypeToClass($type); |
||
172 | preg_match_all( |
||
173 | '/"(?<key>[^"]+)"\s*=\s*"(?<value>[^"]+)"/', |
||
174 | $data, |
||
175 | $matches |
||
176 | ); |
||
177 | $fields = $this->convertFields( |
||
178 | $class, |
||
179 | array_combine($matches['key'], $matches['value']) |
||
180 | ); |
||
181 | $fields = $this->prepareFixture($class, $id, $fields); |
||
182 | // We should check if this fixture object already exists - if it does, we update it. If not, we create it |
||
183 | View Code Duplication | if ($existingFixture = $this->fixtureFactory->get($class, $id)) { |
|
184 | // Merge existing data with new data, and create new object to replace existing object |
||
185 | foreach ($fields as $k => $v) { |
||
186 | $existingFixture->$k = $v; |
||
187 | } |
||
188 | $existingFixture->write(); |
||
189 | } else { |
||
190 | $this->fixtureFactory->createObject($class, $id, $fields); |
||
191 | } |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Example: And the "page" "Page 2" has the following data |
||
196 | * | Content | <blink> | |
||
197 | * | My Property | foo | |
||
198 | * | My Boolean | bar | |
||
199 | * |
||
200 | * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" has the following data$/ |
||
201 | */ |
||
202 | public function stepCreateRecordWithTable($type, $id, $null, TableNode $fieldsTable) |
||
203 | { |
||
204 | $class = $this->convertTypeToClass($type); |
||
205 | // TODO Support more than one record |
||
206 | $fields = $this->convertFields($class, $fieldsTable->getRowsHash()); |
||
207 | $fields = $this->prepareFixture($class, $id, $fields); |
||
208 | |||
209 | // We should check if this fixture object already exists - if it does, we update it. If not, we create it |
||
210 | View Code Duplication | if ($existingFixture = $this->fixtureFactory->get($class, $id)) { |
|
0 ignored issues
–
show
|
|||
211 | // Merge existing data with new data, and create new object to replace existing object |
||
212 | foreach ($fields as $k => $v) { |
||
213 | $existingFixture->$k = $v; |
||
214 | } |
||
215 | $existingFixture->write(); |
||
216 | } else { |
||
217 | $this->fixtureFactory->createObject($class, $id, $fields); |
||
218 | } |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * Example: Given the "page" "Page 1.1" is a child of the "page" "Page1". |
||
223 | * Note that this change is not published by default |
||
224 | * |
||
225 | * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is a (?<relation>[^\s]*) of (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"/ |
||
226 | */ |
||
227 | public function stepUpdateRecordRelation($type, $id, $relation, $relationType, $relationId) |
||
228 | { |
||
229 | $class = $this->convertTypeToClass($type); |
||
230 | |||
231 | $relationClass = $this->convertTypeToClass($relationType); |
||
232 | $relationObj = $this->fixtureFactory->get($relationClass, $relationId); |
||
233 | if (!$relationObj) { |
||
234 | $relationObj = $this->fixtureFactory->createObject($relationClass, $relationId); |
||
235 | } |
||
236 | |||
237 | $data = array(); |
||
238 | if ($relation == 'child') { |
||
239 | $data['ParentID'] = $relationObj->ID; |
||
240 | } |
||
241 | |||
242 | $obj = $this->fixtureFactory->get($class, $id); |
||
243 | if ($obj) { |
||
244 | $obj->update($data); |
||
245 | $obj->write(); |
||
246 | } else { |
||
247 | $obj = $this->fixtureFactory->createObject($class, $id, $data); |
||
248 | } |
||
249 | |||
250 | switch ($relation) { |
||
251 | case 'parent': |
||
252 | $relationObj->ParentID = $obj->ID; |
||
253 | $relationObj->write(); |
||
254 | break; |
||
255 | case 'child': |
||
256 | // already written through $data above |
||
257 | break; |
||
258 | default: |
||
259 | throw new \InvalidArgumentException(sprintf( |
||
260 | 'Invalid relation "%s"', |
||
261 | $relation |
||
262 | )); |
||
263 | } |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Assign a type of object to another type of object. The base object will be created if it does not exist already. |
||
268 | * If the last part of the string (in the "X" relation) is omitted, then the first matching relation will be used. |
||
269 | * |
||
270 | * @example I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1" |
||
271 | * @Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"$/ |
||
272 | */ |
||
273 | public function stepIAssignObjToObj($type, $value, $relationType, $relationId) |
||
274 | { |
||
275 | self::stepIAssignObjToObjInTheRelation($type, $value, $relationType, $relationId, null); |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Assign a type of object to another type of object. The base object will be created if it does not exist already. |
||
280 | * If the last part of the string (in the "X" relation) is omitted, then the first matching relation will be used. |
||
281 | * Assumption: one object has relationship (has_one, has_many or many_many ) with the other object |
||
282 | * |
||
283 | * @example I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1" in the "Terms" relation |
||
284 | * @Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)" in the "(?<relationName>[^"]+)" relation$/ |
||
285 | */ |
||
286 | public function stepIAssignObjToObjInTheRelation($type, $value, $relationType, $relationId, $relationName) |
||
287 | { |
||
288 | $class = $this->convertTypeToClass($type); |
||
289 | $relationClass = $this->convertTypeToClass($relationType); |
||
290 | |||
291 | // Check if this fixture object already exists - if not, we create it |
||
292 | $relationObj = $this->fixtureFactory->get($relationClass, $relationId); |
||
293 | if (!$relationObj) { |
||
294 | $relationObj = $this->fixtureFactory->createObject($relationClass, $relationId); |
||
295 | } |
||
296 | |||
297 | // Check if there is relationship defined in many_many (includes belongs_many_many) |
||
298 | $manyField = null; |
||
299 | $oneField = null; |
||
300 | View Code Duplication | if ($relationObj->many_many()) { |
|
301 | $manyField = array_search($class, $relationObj->many_many()); |
||
302 | if ($manyField && strlen($relationName) > 0) { |
||
303 | $manyField = $relationName; |
||
304 | } |
||
305 | } |
||
306 | View Code Duplication | if (empty($manyField) && $relationObj->has_many()) { |
|
307 | $manyField = array_search($class, $relationObj->has_many()); |
||
308 | if ($manyField && strlen($relationName) > 0) { |
||
309 | $manyField = $relationName; |
||
310 | } |
||
311 | } |
||
312 | View Code Duplication | if (empty($manyField) && $relationObj->has_one()) { |
|
313 | $oneField = array_search($class, $relationObj->has_one()); |
||
314 | if ($oneField && strlen($relationName) > 0) { |
||
315 | $oneField = $relationName; |
||
316 | } |
||
317 | } |
||
318 | if (empty($manyField) && empty($oneField)) { |
||
319 | throw new \Exception("'$relationClass' has no relationship (has_one, has_many and many_many) with '$class'!"); |
||
320 | } |
||
321 | |||
322 | // Get the searchable field to check if the fixture object already exists |
||
323 | $temObj = new $class; |
||
324 | if (isset($temObj->Name)) { |
||
325 | $field = "Name"; |
||
326 | } elseif (isset($temObj->Title)) { |
||
327 | $field = "Title"; |
||
328 | } else { |
||
329 | $field = "ID"; |
||
330 | } |
||
331 | |||
332 | // Check if the fixture object exists - if not, we create it |
||
333 | $obj = DataObject::get($class)->filter($field, $value)->first(); |
||
334 | if (!$obj) { |
||
335 | $obj = $this->fixtureFactory->createObject($class, $value); |
||
336 | } |
||
337 | // If has_many or many_many, add this fixture object to the relation object |
||
338 | // If has_one, set value to the joint field with this fixture object's ID |
||
339 | if ($manyField) { |
||
340 | $relationObj->$manyField()->add($obj); |
||
341 | } elseif ($oneField) { |
||
342 | // E.g. $has_one = array('PanelOffer' => 'Offer'); |
||
343 | // then the join field is PanelOfferID. This is the common rule in the CMS |
||
344 | $relationObj->{$oneField . 'ID'} = $obj->ID; |
||
345 | } |
||
346 | |||
347 | $relationObj->write(); |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Example: Given the "page" "Page 1" is not published |
||
352 | * |
||
353 | * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is (?<state>[^"]*)$/ |
||
354 | */ |
||
355 | public function stepUpdateRecordState($type, $id, $state) |
||
356 | { |
||
357 | $class = $this->convertTypeToClass($type); |
||
358 | /** @var DataObject|Versioned $obj */ |
||
359 | $obj = $this->fixtureFactory->get($class, $id); |
||
360 | if (!$obj) { |
||
361 | throw new \InvalidArgumentException(sprintf( |
||
362 | 'Can not find record "%s" with identifier "%s"', |
||
363 | $type, |
||
364 | $id |
||
365 | )); |
||
366 | } |
||
367 | |||
368 | switch ($state) { |
||
369 | case 'published': |
||
370 | $obj->copyVersionToStage('Stage', 'Live'); |
||
371 | break; |
||
372 | case 'not published': |
||
373 | case 'unpublished': |
||
374 | $oldMode = Versioned::get_reading_mode(); |
||
375 | Versioned::set_stage(Versioned::LIVE); |
||
376 | $clone = clone $obj; |
||
377 | $clone->delete(); |
||
378 | Versioned::set_reading_mode($oldMode); |
||
379 | break; |
||
380 | case 'deleted': |
||
381 | $obj->delete(); |
||
382 | break; |
||
383 | default: |
||
384 | throw new \InvalidArgumentException(sprintf( |
||
385 | 'Invalid state: "%s"', |
||
386 | $state |
||
387 | )); |
||
388 | } |
||
389 | } |
||
390 | |||
391 | /** |
||
392 | * Accepts YAML fixture definitions similar to the ones used in SilverStripe unit testing. |
||
393 | * |
||
394 | * Example: Given there are the following member records: |
||
395 | * member1: |
||
396 | * Email: [email protected] |
||
397 | * member2: |
||
398 | * Email: [email protected] |
||
399 | * |
||
400 | * @Given /^there are the following ([^\s]*) records$/ |
||
401 | */ |
||
402 | public function stepThereAreTheFollowingRecords($dataObject, PyStringNode $string) |
||
403 | { |
||
404 | $yaml = array_merge(array($dataObject . ':'), $string->getLines()); |
||
405 | $yaml = implode("\n ", $yaml); |
||
406 | |||
407 | // Save fixtures into database |
||
408 | // TODO Run prepareAsset() for each File and Folder record |
||
409 | $yamlFixture = new \YamlFixture($yaml); |
||
410 | $yamlFixture->writeInto($this->getFixtureFactory()); |
||
411 | } |
||
412 | |||
413 | /** |
||
414 | * Example: Given a "member" "Admin" belonging to "Admin Group" |
||
415 | * |
||
416 | * @Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)"$/ |
||
417 | */ |
||
418 | public function stepCreateMemberWithGroup($id, $groupId) |
||
419 | { |
||
420 | $group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $groupId); |
||
421 | if (!$group) { |
||
422 | $group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $groupId); |
||
423 | } |
||
424 | |||
425 | $member = $this->fixtureFactory->createObject('SilverStripe\\Security\\Member', $id); |
||
426 | $member->Groups()->add($group); |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * Example: Given a "member" "Admin" belonging to "Admin Group" with "Email"="[email protected]" |
||
431 | * |
||
432 | * @Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)" with (?<data>.*)$/ |
||
433 | */ |
||
434 | public function stepCreateMemberWithGroupAndData($id, $groupId, $data) |
||
435 | { |
||
436 | $class = 'SilverStripe\\Security\\Member'; |
||
437 | preg_match_all( |
||
438 | '/"(?<key>[^"]+)"\s*=\s*"(?<value>[^"]+)"/', |
||
439 | $data, |
||
440 | $matches |
||
441 | ); |
||
442 | $fields = $this->convertFields( |
||
443 | $class, |
||
444 | array_combine($matches['key'], $matches['value']) |
||
445 | ); |
||
446 | |||
447 | $group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $groupId); |
||
448 | if (!$group) { |
||
449 | $group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $groupId); |
||
450 | } |
||
451 | |||
452 | $member = $this->fixtureFactory->createObject($class, $id, $fields); |
||
453 | $member->Groups()->add($group); |
||
454 | } |
||
455 | |||
456 | /** |
||
457 | * Example: Given a "group" "Admin" with permissions "Access to 'Pages' section" and "Access to 'Files' section" |
||
458 | * |
||
459 | * @Given /^(?:(an|a|the) )"group" "(?<id>[^"]+)" (?:(with|has)) permissions (?<permissionStr>.*)$/ |
||
460 | */ |
||
461 | public function stepCreateGroupWithPermissions($id, $permissionStr) |
||
462 | { |
||
463 | // Convert natural language permissions to codes |
||
464 | preg_match_all('/"([^"]+)"/', $permissionStr, $matches); |
||
465 | $permissions = $matches[1]; |
||
466 | $codes = Permission::get_codes(false); |
||
467 | |||
468 | $group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $id); |
||
469 | if (!$group) { |
||
470 | $group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $id); |
||
471 | } |
||
472 | |||
473 | foreach ($permissions as $permission) { |
||
474 | $found = false; |
||
475 | foreach ($codes as $code => $details) { |
||
476 | if ($permission == $code |
||
477 | || $permission == $details['name'] |
||
478 | ) { |
||
479 | Permission::grant($group->ID, $code); |
||
480 | $found = true; |
||
481 | } |
||
482 | } |
||
483 | if (!$found) { |
||
484 | throw new \InvalidArgumentException(sprintf( |
||
485 | 'No permission found for "%s"', |
||
486 | $permission |
||
487 | )); |
||
488 | } |
||
489 | } |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * Navigates to a record based on its identifier set during fixture creation, |
||
494 | * using its RelativeLink() method to map the record to a URL. |
||
495 | * Example: Given I go to the "page" "My Page" |
||
496 | * |
||
497 | * @Given /^I go to (?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)"/ |
||
498 | */ |
||
499 | public function stepGoToNamedRecord($type, $id) |
||
500 | { |
||
501 | $class = $this->convertTypeToClass($type); |
||
502 | $record = $this->fixtureFactory->get($class, $id); |
||
503 | if (!$record) { |
||
504 | throw new \InvalidArgumentException(sprintf( |
||
505 | 'Cannot resolve reference "%s", no matching fixture found', |
||
506 | $id |
||
507 | )); |
||
508 | } |
||
509 | if (!$record->hasMethod('RelativeLink')) { |
||
510 | throw new \InvalidArgumentException('URL for record cannot be determined, missing RelativeLink() method'); |
||
511 | } |
||
512 | |||
513 | $this->getSession()->visit($this->getMainContext()->locatePath($record->RelativeLink())); |
||
514 | } |
||
515 | |||
516 | |||
517 | /** |
||
518 | * Checks that a file or folder exists in the webroot. |
||
519 | * Example: There should be a file "assets/Uploads/test.jpg" |
||
520 | * |
||
521 | * @Then /^there should be a (?<type>(file|folder) )"(?<path>[^"]*)"/ |
||
522 | */ |
||
523 | public function stepThereShouldBeAFileOrFolder($type, $path) |
||
524 | { |
||
525 | assertFileExists($this->joinPaths(BASE_PATH, $path)); |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * Checks that a file exists in the asset store with a given filename and hash |
||
530 | * |
||
531 | * Example: there should be a filename "Uploads/test.jpg" with hash "59de0c841f" |
||
532 | * |
||
533 | * @Then /^there should be a filename "(?<filename>[^"]*)" with hash "(?<hash>[a-fA-Z0-9]+)"/ |
||
534 | */ |
||
535 | public function stepThereShouldBeAFileWithTuple($filename, $hash) |
||
536 | { |
||
537 | $exists = $this->getAssetStore()->exists($filename, $hash); |
||
538 | assertTrue((bool)$exists, "A file exists with filename $filename and hash $hash"); |
||
539 | } |
||
540 | |||
541 | /** |
||
542 | * Replaces fixture references in values with their respective database IDs, |
||
543 | * with the notation "=><class>.<identifier>". Example: "=>Page.My Page". |
||
544 | * |
||
545 | * @Transform /^([^"]+)$/ |
||
546 | */ |
||
547 | public function lookupFixtureReference($string) |
||
548 | { |
||
549 | if (preg_match('/^=>/', $string)) { |
||
550 | list($className, $identifier) = explode('.', preg_replace('/^=>/', '', $string), 2); |
||
551 | $id = $this->fixtureFactory->getId($className, $identifier); |
||
552 | if (!$id) { |
||
553 | throw new \InvalidArgumentException(sprintf( |
||
554 | 'Cannot resolve reference "%s", no matching fixture found', |
||
555 | $string |
||
556 | )); |
||
557 | } |
||
558 | return $id; |
||
559 | } else { |
||
560 | return $string; |
||
561 | } |
||
562 | } |
||
563 | |||
564 | /** |
||
565 | * @Given /^(?:(an|a|the) )"(?<type>[^"]*)" "(?<id>[^"]*)" was (?<mod>(created|last edited)) "(?<time>[^"]*)"$/ |
||
566 | */ |
||
567 | public function aRecordWasLastEditedRelative($type, $id, $mod, $time) |
||
568 | { |
||
569 | $class = $this->convertTypeToClass($type); |
||
570 | $fields = $this->prepareFixture($class, $id); |
||
571 | $record = $this->fixtureFactory->createObject($class, $id, $fields); |
||
572 | $date = date("Y-m-d H:i:s", strtotime($time)); |
||
573 | $table = $record->baseTable(); |
||
574 | $field = ($mod == 'created') ? 'Created' : 'LastEdited'; |
||
575 | DB::prepared_query( |
||
576 | "UPDATE \"{$table}\" SET \"{$field}\" = ? WHERE \"ID\" = ?", |
||
577 | [$date, $record->ID] |
||
578 | ); |
||
579 | // Support for Versioned extension, by checking for a "Live" stage |
||
580 | if (DB::get_schema()->hasTable($table . '_Live')) { |
||
581 | DB::prepared_query( |
||
582 | "UPDATE \"{$table}_Live\" SET \"{$field}\" = ? WHERE \"ID\" = ?", |
||
583 | [$date, $record->ID] |
||
584 | ); |
||
585 | } |
||
586 | } |
||
587 | |||
588 | /** |
||
589 | * Prepares a fixture for use |
||
590 | * |
||
591 | * @param string $class |
||
592 | * @param string $identifier |
||
593 | * @param array $data |
||
594 | * @return array Prepared $data with additional injected fields |
||
595 | */ |
||
596 | protected function prepareFixture($class, $identifier, $data = array()) |
||
597 | { |
||
598 | if ($class == 'File' || is_subclass_of($class, 'File')) { |
||
599 | $data = $this->prepareAsset($class, $identifier, $data); |
||
600 | } |
||
601 | return $data; |
||
602 | } |
||
603 | |||
604 | protected function prepareAsset($class, $identifier, $data = null) |
||
605 | { |
||
606 | if (!$data) { |
||
607 | $data = array(); |
||
608 | } |
||
609 | $relativeTargetPath = (isset($data['Filename'])) ? $data['Filename'] : $identifier; |
||
610 | $relativeTargetPath = preg_replace('/^' . ASSETS_DIR . '\/?/', '', $relativeTargetPath); |
||
611 | $sourcePath = $this->joinPaths($this->getFilesPath(), basename($relativeTargetPath)); |
||
612 | |||
613 | // Create file or folder on filesystem |
||
614 | if ($class == 'Folder' || is_subclass_of($class, 'Folder')) { |
||
615 | $parent = \Folder::find_or_make($relativeTargetPath); |
||
616 | $data['ID'] = $parent->ID; |
||
617 | } else { |
||
618 | $parent = \Folder::find_or_make(dirname($relativeTargetPath)); |
||
619 | if (!file_exists($sourcePath)) { |
||
620 | throw new \InvalidArgumentException(sprintf( |
||
621 | 'Source file for "%s" cannot be found in "%s"', |
||
622 | $relativeTargetPath, |
||
623 | $sourcePath |
||
624 | )); |
||
625 | } |
||
626 | $data['ParentID'] = $parent->ID; |
||
627 | |||
628 | // Load file into APL and retrieve tuple |
||
629 | $asset = $this->getAssetStore()->setFromLocalFile( |
||
630 | $sourcePath, |
||
631 | $relativeTargetPath, |
||
632 | null, |
||
633 | null, |
||
634 | array( |
||
635 | 'conflict' => AssetStore::CONFLICT_OVERWRITE, |
||
636 | 'visibility' => AssetStore::VISIBILITY_PUBLIC |
||
637 | ) |
||
638 | ); |
||
639 | $data['FileFilename'] = $asset['Filename']; |
||
640 | $data['FileHash'] = $asset['Hash']; |
||
641 | $data['FileVariant'] = $asset['Variant']; |
||
642 | } |
||
643 | if (!isset($data['Name'])) { |
||
644 | $data['Name'] = basename($relativeTargetPath); |
||
645 | } |
||
646 | |||
647 | // Save assets |
||
648 | if (isset($data['FileFilename'])) { |
||
649 | $this->createdAssets[] = $data; |
||
650 | } |
||
651 | |||
652 | return $data; |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * |
||
657 | * @return AssetStore |
||
658 | */ |
||
659 | protected function getAssetStore() |
||
660 | { |
||
661 | return singleton('AssetStore'); |
||
662 | } |
||
663 | |||
664 | /** |
||
665 | * Converts a natural language class description to an actual class name. |
||
666 | * Respects {@link DataObject::$singular_name} variations. |
||
667 | * Example: "redirector page" -> "RedirectorPage" |
||
668 | * |
||
669 | * @param String |
||
670 | * @return String Class name |
||
671 | */ |
||
672 | protected function convertTypeToClass($type) |
||
673 | { |
||
674 | $type = trim($type); |
||
675 | |||
676 | // Try direct mapping |
||
677 | $class = str_replace(' ', '', ucwords($type)); |
||
678 | if (class_exists($class) && is_subclass_of($class, 'SilverStripe\\ORM\\DataObject')) { |
||
679 | return \ClassInfo::class_name($class); |
||
680 | } |
||
681 | |||
682 | // Fall back to singular names |
||
683 | foreach (array_values(\ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject')) as $candidate) { |
||
684 | if (strcasecmp(singleton($candidate)->singular_name(), $type) === 0) { |
||
685 | return $candidate; |
||
686 | } |
||
687 | } |
||
688 | |||
689 | throw new \InvalidArgumentException(sprintf( |
||
690 | 'Class "%s" does not exist, or is not a subclass of DataObjet', |
||
691 | $class |
||
692 | )); |
||
693 | } |
||
694 | |||
695 | /** |
||
696 | * Updates an object with values, resolving aliases set through |
||
697 | * {@link DataObject->fieldLabels()}. |
||
698 | * |
||
699 | * @param string $class Class name |
||
700 | * @param array $fields Map of field names or aliases to their values. |
||
701 | * @return array Map of actual object properties to their values. |
||
702 | */ |
||
703 | protected function convertFields($class, $fields) |
||
704 | { |
||
705 | $labels = singleton($class)->fieldLabels(); |
||
706 | foreach ($fields as $fieldName => $fieldVal) { |
||
707 | if ($fieldLabelKey = array_search($fieldName, $labels)) { |
||
708 | unset($fields[$fieldName]); |
||
709 | $fields[$labels[$fieldLabelKey]] = $fieldVal; |
||
710 | } |
||
711 | } |
||
712 | return $fields; |
||
713 | } |
||
714 | |||
715 | protected function joinPaths() |
||
716 | { |
||
717 | $args = func_get_args(); |
||
718 | $paths = array(); |
||
719 | foreach ($args as $arg) { |
||
720 | $paths = array_merge($paths, (array)$arg); |
||
721 | } |
||
722 | foreach ($paths as &$path) { |
||
723 | $path = trim($path, '/'); |
||
724 | } |
||
725 | if (substr($args[0], 0, 1) == '/') { |
||
726 | $paths[0] = '/' . $paths[0]; |
||
727 | } |
||
728 | return join('/', $paths); |
||
729 | } |
||
730 | } |
||
731 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.