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 | * DNProject represents a project that relates to a group of target |
||
5 | * environments. |
||
6 | * |
||
7 | * @property string Name |
||
8 | * @property string CVSPath |
||
9 | * @property int DiskQuotaMB |
||
10 | * |
||
11 | * @method HasManyList Environments() |
||
12 | * @method ManyManyList Viewers() |
||
13 | * @method ManyManyList StarredBy() |
||
14 | */ |
||
15 | class DNProject extends DataObject { |
||
0 ignored issues
–
show
The property $many_many is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $summary_fields is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $searchable_fields is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $singular_name is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $plural_name is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $default_sort is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $show_repository_url is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $relation_cache is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $has_cloned_cache is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $_current_member_cache is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() The property $repository_interfaces is not named in camelCase.
This check marks property names that have not been written in camelCase. In camelCase names are written without any punctuation, the start of each new word being marked
by a capital letter. Thus the name database connection string becomes ![]() |
|||
16 | |||
17 | /** |
||
18 | * @var array |
||
19 | */ |
||
20 | private static $db = [ |
||
0 ignored issues
–
show
|
|||
21 | "Name" => "Varchar", |
||
22 | "IsNewDeployEnabled" => "Boolean", |
||
23 | "CVSPath" => "Varchar(255)", |
||
24 | "DiskQuotaMB" => "Int", |
||
25 | "AllowedEnvironmentType" => "Varchar(255)" |
||
26 | ]; |
||
27 | |||
28 | /** |
||
29 | * @var array |
||
30 | */ |
||
31 | private static $has_many = [ |
||
0 ignored issues
–
show
|
|||
32 | "Environments" => "DNEnvironment", |
||
33 | "CreateEnvironments" => "DNCreateEnvironment", |
||
34 | "Fetches" => "DNGitFetch" |
||
35 | ]; |
||
36 | |||
37 | /** |
||
38 | * @var array |
||
39 | */ |
||
40 | private static $many_many = [ |
||
0 ignored issues
–
show
|
|||
41 | "Viewers" => "Group", |
||
42 | "StarredBy" => "Member" |
||
43 | ]; |
||
44 | |||
45 | /** |
||
46 | * @var array |
||
47 | */ |
||
48 | private static $summary_fields = [ |
||
0 ignored issues
–
show
|
|||
49 | "Name", |
||
50 | "ViewersList", |
||
51 | ]; |
||
52 | |||
53 | /** |
||
54 | * @var array |
||
55 | */ |
||
56 | private static $searchable_fields = [ |
||
0 ignored issues
–
show
|
|||
57 | "Name", |
||
58 | ]; |
||
59 | |||
60 | /** |
||
61 | * @var string |
||
62 | */ |
||
63 | private static $singular_name = 'Project'; |
||
0 ignored issues
–
show
|
|||
64 | |||
65 | /** |
||
66 | * @var string |
||
67 | */ |
||
68 | private static $plural_name = 'Projects'; |
||
69 | |||
70 | /** |
||
71 | * @var string |
||
72 | */ |
||
73 | private static $default_sort = 'Name'; |
||
74 | |||
75 | /** |
||
76 | * In-memory cache for currentBuilds per environment since fetching them from |
||
77 | * disk is pretty resource hungry. |
||
78 | * |
||
79 | * @var array |
||
80 | */ |
||
81 | protected static $relation_cache = []; |
||
82 | |||
83 | /** |
||
84 | * @var bool|Member |
||
85 | */ |
||
86 | protected static $_current_member_cache = null; |
||
87 | |||
88 | /** |
||
89 | * Display the repository URL on the project page. |
||
90 | * |
||
91 | * @var bool |
||
92 | */ |
||
93 | private static $show_repository_url = false; |
||
0 ignored issues
–
show
|
|||
94 | |||
95 | /** |
||
96 | * In-memory cache to determine whether clone repo was called. |
||
97 | * @var array |
||
98 | */ |
||
99 | private static $has_cloned_cache = []; |
||
100 | |||
101 | /** |
||
102 | * Whitelist configuration that describes how to convert a repository URL into a link |
||
103 | * to a web user interface for that URL |
||
104 | * |
||
105 | * Consists of a hash of "full.lower.case.domain" => {configuration} key/value pairs |
||
106 | * |
||
107 | * {configuration} can either be boolean true to auto-detect both the host and the |
||
108 | * name of the UI provider, or a nested array that overrides either one or both |
||
109 | * of the auto-detected values |
||
110 | * |
||
111 | * @var array |
||
112 | */ |
||
113 | private static $repository_interfaces = [ |
||
0 ignored issues
–
show
|
|||
114 | 'github.com' => [ |
||
115 | 'icon' => 'deploynaut/img/github.png', |
||
116 | 'name' => 'Github.com', |
||
117 | ], |
||
118 | 'bitbucket.org' => [ |
||
119 | 'commit' => 'commits', |
||
120 | 'name' => 'Bitbucket.org', |
||
121 | ], |
||
122 | 'repo.or.cz' => [ |
||
123 | 'scheme' => 'http', |
||
124 | 'name' => 'repo.or.cz', |
||
125 | 'regex' => ['^(.*)$' => '/w$1'], |
||
126 | ], |
||
127 | |||
128 | /* Example for adding your own gitlab repository and override all auto-detected values (with their defaults) |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
38% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
129 | 'gitlab.mysite.com' => array( |
||
130 | 'icon' => 'deploynaut/img/git.png', |
||
131 | 'host' => 'gitlab.mysite.com', |
||
132 | 'name' => 'Gitlab', |
||
133 | 'regex' => array('.git$' => ''), |
||
134 | 'commit' => "commit" |
||
135 | ), |
||
136 | */ |
||
137 | ]; |
||
138 | |||
139 | /** |
||
140 | * Used by the sync task |
||
141 | * |
||
142 | * @param string $path |
||
143 | * @return \DNProject |
||
144 | */ |
||
145 | public static function create_from_path($path) { |
||
146 | $project = DNProject::create(); |
||
0 ignored issues
–
show
As per coding style,
self should be used for accessing local static members.
This check looks for accesses to local static members using the fully qualified name instead
of <?php
class Certificate {
const TRIPLEDES_CBC = 'ASDFGHJKL';
private $key;
public function __construct()
{
$this->key = Certificate::TRIPLEDES_CBC;
}
}
While this is perfectly valid, the fully qualified name of ![]() |
|||
147 | $project->Name = $path; |
||
148 | $project->write(); |
||
149 | |||
150 | // add the administrators group as the viewers of the new project |
||
151 | $adminGroup = Group::get()->filter('Code', 'administrators')->first(); |
||
152 | if ($adminGroup && $adminGroup->exists()) { |
||
153 | $project->Viewers()->add($adminGroup); |
||
154 | } |
||
155 | return $project; |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * This will clear the cache for the git getters and should be called when the local git repo is updated |
||
160 | */ |
||
161 | public function clearGitCache() { |
||
162 | $cache = self::get_git_cache(); |
||
163 | // we only need to clear the tag cache since everything else is cached by SHA, that is for commit and |
||
164 | // commit message. |
||
165 | $cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, ['gitonomy', 'tags', 'project_' . $this->ID]); |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * @return \Zend_Cache_Frontend_Output |
||
170 | */ |
||
171 | public static function get_git_cache() { |
||
172 | return SS_Cache::factory('gitonomy', 'Output', [ |
||
173 | 'automatic_serialization' => true, |
||
174 | 'lifetime' => 60 * 60 * 24 * 7 // seven days |
||
175 | ]); |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * Return the used quota in MB. |
||
180 | * |
||
181 | * @param int $round Number of decimal places to round to |
||
182 | * @return double The used quota size in MB |
||
183 | */ |
||
184 | public function getUsedQuotaMB($round = 2) { |
||
185 | $size = 0; |
||
186 | |||
187 | foreach ($this->Environments() as $environment) { |
||
188 | foreach ($environment->DataArchives()->filter('IsBackup', 0) as $archive) { |
||
189 | $size += $archive->ArchiveFile()->getAbsoluteSize(); |
||
190 | } |
||
191 | } |
||
192 | |||
193 | // convert bytes to megabytes and round |
||
194 | return round(($size / 1024) / 1024, $round); |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Getter for DiskQuotaMB field to provide a default for existing |
||
199 | * records that have no quota field set, as it will need to default |
||
200 | * to a globally set size. |
||
201 | * |
||
202 | * @return string|int The quota size in MB |
||
203 | */ |
||
204 | public function getDiskQuotaMB() { |
||
205 | $size = $this->getField('DiskQuotaMB'); |
||
206 | |||
207 | if (empty($size)) { |
||
208 | $defaults = $this->config()->get('defaults'); |
||
209 | $size = (isset($defaults['DiskQuotaMB'])) ? $defaults['DiskQuotaMB'] : 0; |
||
210 | } |
||
211 | |||
212 | return $size; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * @return string |
||
217 | */ |
||
218 | public function getPrimaryEnvType() { |
||
219 | $envClasses = $this->Environments()->column("ClassName"); |
||
220 | $topClass = array_count_values($envClasses); |
||
221 | return str_replace("Environment", "", array_search(max($topClass), $topClass)); |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * Has the disk quota been exceeded? |
||
226 | * |
||
227 | * @return boolean |
||
228 | */ |
||
229 | public function HasExceededDiskQuota() { |
||
230 | return $this->getUsedQuotaMB(0) >= $this->getDiskQuotaMB(); |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Is there a disk quota set for this project? |
||
235 | * |
||
236 | * @return boolean |
||
237 | */ |
||
238 | public function HasDiskQuota() { |
||
239 | return $this->getDiskQuotaMB() > 0; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Returns the current disk quota usage as a percentage |
||
244 | * |
||
245 | * @return int |
||
246 | */ |
||
247 | public function DiskQuotaUsagePercent() { |
||
248 | $quota = $this->getDiskQuotaMB(); |
||
249 | if ($quota > 0) { |
||
250 | return $this->getUsedQuotaMB() * 100 / $quota; |
||
251 | } |
||
252 | return 100; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Get the menu to be shown on projects |
||
257 | * |
||
258 | * @return ArrayList |
||
259 | */ |
||
260 | public function Menu() { |
||
261 | $list = new ArrayList(); |
||
262 | |||
263 | $controller = Controller::curr(); |
||
264 | $actionType = $controller->getField('CurrentActionType'); |
||
265 | |||
266 | if ($this->isProjectReady()) { |
||
267 | $list->push(new ArrayData([ |
||
268 | 'Link' => $this->Link('snapshots'), |
||
269 | 'Title' => 'Snapshots', |
||
270 | 'IsCurrent' => $this->isSection() && $controller->getAction() == 'snapshots', |
||
271 | 'IsSection' => $this->isSection() && $actionType == DNRoot::ACTION_SNAPSHOT |
||
272 | ])); |
||
273 | } |
||
274 | |||
275 | $this->extend('updateMenu', $list); |
||
276 | |||
277 | return $list; |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * Is this project currently at the root level of the controller that handles it? |
||
282 | * |
||
283 | * @return bool |
||
284 | */ |
||
285 | public function isCurrent() { |
||
286 | return $this->isSection() && Controller::curr()->getAction() == 'project'; |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * Return the current object from $this->Menu() |
||
291 | * Good for making titles and things |
||
292 | * |
||
293 | * @return DataObject |
||
294 | */ |
||
295 | public function CurrentMenu() { |
||
296 | return $this->Menu()->filter('IsSection', true)->First(); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Is this project currently in a controller that is handling it or performing a sub-task? |
||
301 | * |
||
302 | * @return bool |
||
303 | */ |
||
304 | public function isSection() { |
||
305 | $controller = Controller::curr(); |
||
306 | $project = $controller->getField('CurrentProject'); |
||
307 | return $project && $this->ID == $project->ID; |
||
308 | } |
||
309 | |||
310 | /** |
||
311 | * Restrict access to viewing this project |
||
312 | * |
||
313 | * @param Member|null $member |
||
314 | * @return boolean |
||
315 | */ |
||
316 | public function canView($member = null) { |
||
317 | if (!$member) { |
||
318 | $member = Member::currentUser(); |
||
319 | } |
||
320 | |||
321 | if (Permission::checkMember($member, 'ADMIN')) { |
||
322 | return true; |
||
323 | } |
||
324 | |||
325 | return $member->inGroups($this->Viewers()); |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * @param Member|null $member |
||
330 | * |
||
331 | * @return bool |
||
332 | */ |
||
333 | View Code Duplication | public function canRestore($member = null) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
334 | if ($this->allowedAny( |
||
335 | [ |
||
336 | DNRoot::ALLOW_PROD_SNAPSHOT, |
||
337 | DNRoot::ALLOW_NON_PROD_SNAPSHOT |
||
338 | ], |
||
339 | $member |
||
340 | ) |
||
341 | ) { |
||
342 | return true; |
||
343 | } |
||
344 | |||
345 | return (bool) $this->Environments()->filterByCallback(function ($env) use ($member) { |
||
346 | return $env->canRestore($member); |
||
347 | })->Count(); |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * @param Member|null $member |
||
352 | * @return bool |
||
353 | */ |
||
354 | View Code Duplication | public function canBackup($member = null) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
355 | if ($this->allowedAny( |
||
356 | [ |
||
357 | DNRoot::ALLOW_PROD_SNAPSHOT, |
||
358 | DNRoot::ALLOW_NON_PROD_SNAPSHOT |
||
359 | ], |
||
360 | $member |
||
361 | ) |
||
362 | ) { |
||
363 | return true; |
||
364 | } |
||
365 | |||
366 | return (bool) $this->Environments()->filterByCallback(function ($env) use ($member) { |
||
367 | return $env->canBackup($member); |
||
368 | })->Count(); |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * @param Member|null $member |
||
373 | * @return bool |
||
374 | */ |
||
375 | View Code Duplication | public function canUploadArchive($member = null) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
376 | if ($this->allowedAny( |
||
377 | [ |
||
378 | DNRoot::ALLOW_PROD_SNAPSHOT, |
||
379 | DNRoot::ALLOW_NON_PROD_SNAPSHOT |
||
380 | ], |
||
381 | $member |
||
382 | ) |
||
383 | ) { |
||
384 | return true; |
||
385 | } |
||
386 | |||
387 | return (bool) $this->Environments()->filterByCallback(function ($env) use ($member) { |
||
388 | return $env->canUploadArchive($member); |
||
389 | })->Count(); |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * @param Member|null $member |
||
394 | * @return bool |
||
395 | */ |
||
396 | View Code Duplication | public function canDownloadArchive($member = null) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
397 | if ($this->allowedAny( |
||
398 | [ |
||
399 | DNRoot::ALLOW_PROD_SNAPSHOT, |
||
400 | DNRoot::ALLOW_NON_PROD_SNAPSHOT |
||
401 | ], |
||
402 | $member |
||
403 | ) |
||
404 | ) { |
||
405 | return true; |
||
406 | } |
||
407 | |||
408 | return (bool) $this->Environments()->filterByCallback(function ($env) use ($member) { |
||
409 | return $env->canDownloadArchive($member); |
||
410 | })->Count(); |
||
411 | } |
||
412 | |||
413 | /** |
||
414 | * This is a permission check for the front-end only. |
||
415 | * |
||
416 | * Only admins can create environments for now. Also, we need to check the value |
||
417 | * of AllowedEnvironmentType which dictates which backend to use to render the form. |
||
418 | * |
||
419 | * @param Member|null $member |
||
420 | * |
||
421 | * @return bool |
||
422 | */ |
||
423 | public function canCreateEnvironments($member = null) { |
||
424 | $envType = $this->AllowedEnvironmentType; |
||
0 ignored issues
–
show
The property
AllowedEnvironmentType does not exist on object<DNProject> . Since you implemented __set , maybe consider adding a @property annotation.
Since your code implements the magic setter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
Since the property has write access only, you can use the @property-write annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() |
|||
425 | if ($envType) { |
||
426 | $env = Injector::inst()->get($envType); |
||
427 | if ($env instanceof EnvironmentCreateBackend) { |
||
428 | return $this->allowed(DNRoot::ALLOW_CREATE_ENVIRONMENT, $member); |
||
429 | } |
||
430 | } |
||
431 | return false; |
||
432 | } |
||
433 | |||
434 | /** |
||
435 | * @return DataList |
||
436 | */ |
||
437 | public function DataArchives() { |
||
438 | $envIds = $this->Environments()->column('ID'); |
||
439 | return DNDataArchive::get()->filter('EnvironmentID', $envIds); |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * Return all archives which are "manual upload requests", |
||
444 | * meaning they don't have a file attached to them (yet). |
||
445 | * |
||
446 | * @return DataList |
||
447 | */ |
||
448 | public function PendingManualUploadDataArchives() { |
||
449 | return $this->DataArchives()->filter('ArchiveFileID', null); |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * Build an environment variable array to be used with this project. |
||
454 | * |
||
455 | * This is relevant if every project needs to use an individual SSH pubkey. |
||
456 | * |
||
457 | * Include this with all Gitonomy\Git\Repository, and |
||
458 | * \Symfony\Component\Process\Processes. |
||
459 | * |
||
460 | * @return array |
||
461 | */ |
||
462 | public function getProcessEnv() { |
||
463 | if (file_exists($this->getPrivateKeyPath())) { |
||
464 | // Key-pair is available, use it. |
||
465 | $processEnv = [ |
||
466 | 'IDENT_KEY' => $this->getPrivateKeyPath(), |
||
467 | 'GIT_SSH' => BASE_PATH . "/deploynaut/git-deploy.sh" |
||
468 | ]; |
||
469 | } else { |
||
470 | $processEnv = []; |
||
471 | } |
||
472 | $this->extend('updateProcessEnv', $processEnv); |
||
473 | |||
474 | return $processEnv; |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * Get a string of people allowed to view this project |
||
479 | * |
||
480 | * @return string |
||
481 | */ |
||
482 | public function getViewersList() { |
||
483 | return implode(", ", $this->Viewers()->column("Title")); |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * @return DNData |
||
488 | */ |
||
489 | public function DNData() { |
||
490 | return DNData::inst(); |
||
491 | } |
||
492 | |||
493 | /** |
||
494 | * Provides a DNBuildList of builds found in this project. |
||
495 | * |
||
496 | * @return DNReferenceList |
||
497 | */ |
||
498 | public function DNBuildList() { |
||
499 | return DNReferenceList::create($this, $this->DNData()); |
||
500 | } |
||
501 | |||
502 | /** |
||
503 | * Provides a list of the branches in this project. |
||
504 | * |
||
505 | * @return DNBranchList |
||
506 | */ |
||
507 | public function DNBranchList() { |
||
508 | if ($this->CVSPath && !$this->repoExists()) { |
||
509 | $this->cloneRepo(); |
||
510 | } |
||
511 | return DNBranchList::create($this, $this->DNData()); |
||
512 | } |
||
513 | |||
514 | /** |
||
515 | * Provides a list of the tags in this project. |
||
516 | * |
||
517 | * @return DNReferenceList |
||
518 | */ |
||
519 | public function DNTagList() { |
||
520 | if ($this->CVSPath && !$this->repoExists()) { |
||
521 | $this->cloneRepo(); |
||
522 | } |
||
523 | return DNReferenceList::create($this, $this->DNData(), null, null, true); |
||
524 | } |
||
525 | |||
526 | /** |
||
527 | * @return false|Gitonomy\Git\Repository |
||
528 | */ |
||
529 | public function getRepository() { |
||
530 | if (!$this->repoExists()) { |
||
531 | return false; |
||
532 | } |
||
533 | |||
534 | return new \Gitonomy\Git\Repository($this->getLocalCVSPath()); |
||
535 | } |
||
536 | |||
537 | /** |
||
538 | * Resolve a git reference like a branch or tag into a SHA. |
||
539 | * @return bool|string |
||
540 | */ |
||
541 | public function resolveRevision($value) { |
||
542 | $repository = $this->getRepository(); |
||
543 | if (!$repository) { |
||
544 | return false; |
||
545 | } |
||
546 | |||
547 | try { |
||
548 | $revision = $this->repository->getRevision($value); |
||
0 ignored issues
–
show
The property
repository does not seem to exist. Did you mean show_repository_url ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
549 | return $revision->getCommit()->getHash(); |
||
550 | } catch (\Gitonomy\Git\Exception\ReferenceNotFoundException $e) { |
||
551 | return false; |
||
552 | } |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Provides a list of environments found in this project. |
||
557 | * CAUTION: filterByCallback will change this into an ArrayList! |
||
558 | * |
||
559 | * @return ArrayList |
||
560 | */ |
||
561 | public function DNEnvironmentList() { |
||
562 | |||
563 | if (!self::$_current_member_cache) { |
||
564 | self::$_current_member_cache = Member::currentUser(); |
||
0 ignored issues
–
show
It seems like
\Member::currentUser() can also be of type object<DataObject> . However, the property $_current_member_cache is declared as type boolean|object<Member> . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
565 | } |
||
566 | |||
567 | if (self::$_current_member_cache === false) { |
||
568 | return new ArrayList(); |
||
569 | } |
||
570 | |||
571 | $currentMember = self::$_current_member_cache; |
||
572 | return $this->Environments() |
||
573 | ->filterByCallBack(function ($item) use ($currentMember) { |
||
574 | return $item->canView($currentMember); |
||
575 | }); |
||
576 | } |
||
577 | |||
578 | /** |
||
579 | * @param string $usage |
||
580 | * @return ArrayList |
||
581 | */ |
||
582 | public function EnvironmentsByUsage($usage) { |
||
583 | return $this->DNEnvironmentList()->filter('Usage', $usage); |
||
584 | } |
||
585 | |||
586 | /** |
||
587 | * Returns a map of envrionment name to build name |
||
588 | * |
||
589 | * @return false|DNDeployment |
||
590 | */ |
||
591 | public function currentBuilds() { |
||
592 | if (!isset(self::$relation_cache['currentBuilds.' . $this->ID])) { |
||
593 | $currentBuilds = []; |
||
594 | foreach ($this->Environments() as $env) { |
||
595 | $currentBuilds[$env->Name] = $env->CurrentBuild(); |
||
596 | } |
||
597 | self::$relation_cache['currentBuilds.' . $this->ID] = $currentBuilds; |
||
598 | } |
||
599 | return self::$relation_cache['currentBuilds.' . $this->ID]; |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * @param string |
||
604 | * @return string |
||
605 | */ |
||
606 | public function Link($action = '') { |
||
607 | return Controller::join_links("naut", "project", $this->Name, $action); |
||
608 | } |
||
609 | |||
610 | /** |
||
611 | * @return string |
||
612 | */ |
||
613 | public function getCMSEditLink() { |
||
614 | return Controller::join_links("admin", "naut", "DNProject", "EditForm", "field", "DNProject", "item", $this->ID, "edit"); |
||
0 ignored issues
–
show
|
|||
615 | } |
||
616 | /** |
||
617 | * @return string|null |
||
618 | */ |
||
619 | public function CreateEnvironmentLink() { |
||
620 | if ($this->canCreateEnvironments()) { |
||
621 | return $this->Link('createenv'); |
||
622 | } |
||
623 | return null; |
||
624 | } |
||
625 | |||
626 | /** |
||
627 | * @return string |
||
628 | */ |
||
629 | public function ToggleStarLink() { |
||
630 | return $this->Link('/star'); |
||
631 | } |
||
632 | |||
633 | /** |
||
634 | * @return bool |
||
635 | */ |
||
636 | public function IsStarred() { |
||
637 | $member = Member::currentUser(); |
||
638 | if ($member === null) { |
||
639 | return false; |
||
640 | } |
||
641 | $favourited = $this->StarredBy()->filter('MemberID', $member->ID); |
||
642 | if ($favourited->count() == 0) { |
||
643 | return false; |
||
644 | } |
||
645 | return true; |
||
646 | } |
||
647 | |||
648 | /** |
||
649 | * @param string $action |
||
650 | * @return string |
||
651 | */ |
||
652 | public function APILink($action) { |
||
653 | return Controller::join_links("naut", "api", $this->Name, $action); |
||
654 | } |
||
655 | |||
656 | /** |
||
657 | * @return FieldList |
||
658 | */ |
||
659 | public function getCMSFields() { |
||
660 | $fields = parent::getCMSFields(); |
||
661 | |||
662 | /** @var GridField $environments */ |
||
663 | $environments = $fields->dataFieldByName("Environments"); |
||
664 | |||
665 | $fields->fieldByName("Root")->removeByName("Viewers"); |
||
666 | $fields->fieldByName("Root")->removeByName("Environments"); |
||
667 | $fields->fieldByName("Root")->removeByName("LocalCVSPath"); |
||
668 | |||
669 | $diskQuotaDesc = 'This is the maximum amount of disk space (in megabytes) that all environments within this ' |
||
670 | . 'project can use for stored snapshots'; |
||
671 | $fields->dataFieldByName('DiskQuotaMB')->setDescription($diskQuotaDesc); |
||
672 | |||
673 | $projectNameDesc = 'Changing the name will <strong>reset</strong> the deploy configuration and avoid using non' |
||
674 | . 'alphanumeric characters'; |
||
675 | $fields->fieldByName('Root.Main.Name') |
||
676 | ->setTitle('Project name') |
||
677 | ->setDescription($projectNameDesc); |
||
678 | |||
679 | $fields->fieldByName('Root.Main.IsNewDeployEnabled') |
||
680 | ->setTitle('New deploy form enabled for this project') |
||
681 | ->setDescription('Feature flag to change links to environment and deployments to the new deployment form for this project'); |
||
0 ignored issues
–
show
|
|||
682 | |||
683 | $fields->fieldByName('Root.Main.CVSPath') |
||
684 | ->setTitle('Git repository') |
||
685 | ->setDescription('E.g. [email protected]:silverstripe/silverstripe-installer.git'); |
||
686 | |||
687 | $workspaceField = new ReadonlyField('LocalWorkspace', 'Git workspace', $this->getLocalCVSPath()); |
||
688 | $workspaceField->setDescription('This is where the GIT repository are located on this server'); |
||
689 | $fields->insertAfter($workspaceField, 'CVSPath'); |
||
0 ignored issues
–
show
'CVSPath' is of type string , but the function expects a object<FormField> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
690 | |||
691 | $readAccessGroups = ListboxField::create('Viewers', 'Project viewers', Group::get()->map()->toArray()) |
||
692 | ->setMultiple(true) |
||
693 | ->setDescription('These groups can view the project in the front-end.'); |
||
694 | $fields->addFieldToTab("Root.Main", $readAccessGroups); |
||
695 | |||
696 | $this->setCreateProjectFolderField($fields); |
||
697 | $this->setEnvironmentFields($fields, $environments); |
||
698 | |||
699 | $environmentTypes = ClassInfo::implementorsOf('EnvironmentCreateBackend'); |
||
700 | $types = []; |
||
701 | foreach ($environmentTypes as $type) { |
||
702 | $types[$type] = $type; |
||
703 | } |
||
704 | |||
705 | $fields->addFieldsToTab('Root.Main', [ |
||
706 | DropdownField::create( |
||
707 | 'AllowedEnvironmentType', |
||
708 | 'Allowed Environment Type', |
||
709 | $types |
||
710 | )->setDescription('This defined which form to show on the front end for ' |
||
711 | . 'environment creation. This will not affect backend functionality.') |
||
712 | ->setEmptyString(' - None - '), |
||
713 | ]); |
||
714 | |||
715 | return $fields; |
||
716 | } |
||
717 | |||
718 | /** |
||
719 | * If there isn't a capistrano env project folder, show options to create one |
||
720 | * |
||
721 | * @param FieldList $fields |
||
722 | */ |
||
723 | public function setCreateProjectFolderField(&$fields) { |
||
724 | // Check if the capistrano project folder exists |
||
725 | if (!$this->Name) { |
||
726 | return; |
||
727 | } |
||
728 | |||
729 | if ($this->projectFolderExists()) { |
||
730 | return; |
||
731 | } |
||
732 | |||
733 | $createFolderNotice = new LabelField('CreateEnvFolderNotice', 'Warning: No Capistrano project folder exists'); |
||
734 | $createFolderNotice->addExtraClass('message warning'); |
||
735 | $fields->insertBefore($createFolderNotice, 'Name'); |
||
0 ignored issues
–
show
'Name' is of type string , but the function expects a object<FormField> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
736 | $createFolderField = new CheckboxField('CreateEnvFolder', 'Create folder'); |
||
737 | $createFolderField->setDescription('Would you like to create the capistrano project folder?'); |
||
738 | $fields->insertAfter($createFolderField, 'CreateEnvFolderNotice'); |
||
739 | } |
||
740 | |||
741 | /** |
||
742 | * @return boolean |
||
743 | */ |
||
744 | public function projectFolderExists() { |
||
745 | return file_exists($this->getProjectFolderPath()); |
||
746 | } |
||
747 | |||
748 | /** |
||
749 | * @return bool |
||
750 | */ |
||
751 | public function repoExists() { |
||
752 | return file_exists(sprintf('%s/HEAD', $this->getLocalCVSPath())); |
||
753 | } |
||
754 | |||
755 | /** |
||
756 | * Setup a job to clone a git repository. |
||
757 | * @return string resque token |
||
758 | */ |
||
759 | public function cloneRepo() { |
||
760 | // Avoid this being called multiple times in the same request |
||
761 | if (!isset(self::$has_cloned_cache[$this->ID])) { |
||
762 | $fetch = DNGitFetch::create(); |
||
763 | $fetch->ProjectID = $this->ID; |
||
764 | $fetch->write(); |
||
765 | |||
766 | // passing true here tells DNGitFetch to force a git clone, otherwise |
||
767 | // it will just update the repo if it already exists. We want to ensure |
||
768 | // we're always cloning a new repo in this case, as the git URL may have changed. |
||
769 | $fetch->start(true); |
||
770 | |||
771 | self::$has_cloned_cache[$this->ID] = true; |
||
772 | } |
||
773 | } |
||
774 | |||
775 | /** |
||
776 | * @return string |
||
777 | */ |
||
778 | public function getLocalCVSPath() { |
||
779 | return sprintf('%s/%s', DEPLOYNAUT_LOCAL_VCS_PATH, $this->Name); |
||
780 | } |
||
781 | |||
782 | public function onBeforeWrite() { |
||
783 | parent::onBeforeWrite(); |
||
784 | |||
785 | if ($this->CreateEnvFolder && !file_exists($this->getProjectFolderPath())) { |
||
0 ignored issues
–
show
The property
CreateEnvFolder does not exist on object<DNProject> . Since you implemented __get , maybe consider adding a @property annotation.
Since your code implements the magic getter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
If the property has read access only, you can use the @property-read annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() |
|||
786 | mkdir($this->getProjectFolderPath()); |
||
787 | } |
||
788 | } |
||
789 | |||
790 | public function onAfterWrite() { |
||
791 | parent::onAfterWrite(); |
||
792 | |||
793 | if (!$this->CVSPath) { |
||
794 | return; |
||
795 | } |
||
796 | |||
797 | $changedFields = $this->getChangedFields(true, 2); |
||
798 | if (isset($changedFields['CVSPath']) || isset($changedFields['Name'])) { |
||
799 | $this->cloneRepo(); |
||
800 | } |
||
801 | } |
||
802 | |||
803 | /** |
||
804 | * Delete related environments and folders |
||
805 | */ |
||
806 | public function onAfterDelete() { |
||
807 | parent::onAfterDelete(); |
||
808 | |||
809 | $environments = $this->Environments(); |
||
810 | if ($environments && $environments->exists()) { |
||
811 | foreach ($environments as $env) { |
||
812 | $env->delete(); |
||
813 | } |
||
814 | } |
||
815 | |||
816 | $fetches = $this->Fetches(); |
||
0 ignored issues
–
show
The method
Fetches does not exist on object<DNProject> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
![]() |
|||
817 | if ($fetches && $fetches->exists()) { |
||
818 | foreach ($fetches as $fetch) { |
||
819 | $fetch->delete(); |
||
820 | } |
||
821 | } |
||
822 | |||
823 | // Delete local repository |
||
824 | if (file_exists($this->getLocalCVSPath())) { |
||
825 | Filesystem::removeFolder($this->getLocalCVSPath()); |
||
826 | } |
||
827 | |||
828 | // Delete project template |
||
829 | if (file_exists($this->getProjectFolderPath()) && Config::inst()->get('DNEnvironment', 'allow_web_editing')) { |
||
830 | Filesystem::removeFolder($this->getProjectFolderPath()); |
||
831 | } |
||
832 | |||
833 | // Delete the deploy key |
||
834 | if (file_exists($this->getKeyDir())) { |
||
835 | Filesystem::removeFolder($this->getKeyDir()); |
||
836 | } |
||
837 | } |
||
838 | |||
839 | /** |
||
840 | * Fetch the public key for this project. |
||
841 | * |
||
842 | * @return string|void |
||
843 | */ |
||
844 | public function getPublicKey() { |
||
845 | $key = $this->getPublicKeyPath(); |
||
846 | |||
847 | if (file_exists($key)) { |
||
848 | return trim(file_get_contents($key)); |
||
849 | } |
||
850 | } |
||
851 | |||
852 | /** |
||
853 | * This returns that path of the public key if a key directory is set. It doesn't check whether the file exists. |
||
854 | * |
||
855 | * @return string|null |
||
856 | */ |
||
857 | public function getPublicKeyPath() { |
||
858 | if ($privateKey = $this->getPrivateKeyPath()) { |
||
859 | return $privateKey . '.pub'; |
||
860 | } |
||
861 | return null; |
||
862 | } |
||
863 | |||
864 | /** |
||
865 | * This returns that path of the private key if a key directory is set. It doesn't check whether the file exists. |
||
866 | * |
||
867 | * @return string|null |
||
868 | */ |
||
869 | public function getPrivateKeyPath() { |
||
870 | $keyDir = $this->getKeyDir(); |
||
871 | if (!empty($keyDir)) { |
||
872 | $filter = FileNameFilter::create(); |
||
873 | $name = $filter->filter($this->Name); |
||
874 | return $keyDir . '/' . $name; |
||
875 | } |
||
876 | return null; |
||
877 | } |
||
878 | |||
879 | /** |
||
880 | * Returns the location of the projects key dir if one exists. |
||
881 | * |
||
882 | * @return string|null |
||
883 | */ |
||
884 | public function getKeyDir() { |
||
885 | $keyDir = $this->DNData()->getKeyDir(); |
||
886 | if (!$keyDir) { |
||
887 | return null; |
||
888 | } |
||
889 | |||
890 | $filter = FileNameFilter::create(); |
||
891 | $name = $filter->filter($this->Name); |
||
892 | |||
893 | return $this->DNData()->getKeyDir() . '/' . $name; |
||
894 | } |
||
895 | |||
896 | /** |
||
897 | * Provide current repository URL to the users. |
||
898 | * |
||
899 | * @return void|string |
||
900 | */ |
||
901 | public function getRepositoryURL() { |
||
902 | $showUrl = Config::inst()->get($this->class, 'show_repository_url'); |
||
903 | if ($showUrl) { |
||
904 | return $this->CVSPath; |
||
905 | } |
||
906 | } |
||
907 | |||
908 | /** |
||
909 | * Get a ViewableData structure describing the UI tool that lets the user view the repository code |
||
910 | * |
||
911 | * @return ArrayData |
||
912 | */ |
||
913 | public function getRepositoryInterface() { |
||
914 | $interfaces = $this->config()->repository_interfaces; |
||
915 | |||
916 | /* Look for each whitelisted hostname */ |
||
917 | foreach ($interfaces as $host => $interface) { |
||
918 | /* See if the CVS Path is for this hostname, followed by some junk (maybe a port), then the path */ |
||
919 | if (preg_match('{^[^.]*' . $host . '(.*?)([/a-zA-Z].+)}', $this->CVSPath, $match)) { |
||
920 | |||
921 | $path = $match[2]; |
||
922 | |||
923 | $scheme = isset($interface['scheme']) ? $interface['scheme'] : 'https'; |
||
924 | $host = isset($interface['host']) ? $interface['host'] : $host; |
||
925 | $regex = isset($interface['regex']) ? $interface['regex'] : ['\.git$' => '']; |
||
926 | |||
927 | $components = explode('.', $host); |
||
928 | |||
929 | foreach ($regex as $pattern => $replacement) { |
||
930 | $path = preg_replace('/' . $pattern . '/', $replacement, $path); |
||
931 | } |
||
932 | |||
933 | $uxurl = Controller::join_links($scheme . '://', $host, $path); |
||
934 | |||
935 | if (array_key_exists('commit', $interface) && $interface['commit'] == false) { |
||
936 | $commiturl = false; |
||
937 | } else { |
||
938 | $commiturl = Controller::join_links( |
||
939 | $uxurl, |
||
940 | isset($interface['commit']) ? $interface['commit'] : 'commit' |
||
941 | ); |
||
942 | } |
||
943 | |||
944 | return new ArrayData([ |
||
945 | 'Name' => isset($interface['name']) ? $interface['name'] : ucfirst($components[0]), |
||
946 | 'Icon' => isset($interface['icon']) ? $interface['icon'] : 'deploynaut/img/git.png', |
||
947 | 'URL' => $uxurl, |
||
948 | 'CommitURL' => $commiturl |
||
949 | ]); |
||
950 | } |
||
951 | } |
||
952 | } |
||
953 | |||
954 | /** |
||
955 | * Convenience wrapper for a single permission code. |
||
956 | * |
||
957 | * @param string $code |
||
958 | * @return SS_List |
||
959 | */ |
||
960 | public function whoIsAllowed($code) { |
||
961 | return $this->whoIsAllowedAny([$code]); |
||
962 | } |
||
963 | |||
964 | /** |
||
965 | * List members who have $codes on this project. |
||
966 | * Does not support Permission::DENY_PERMISSION malarky, same as Permission::get_groups_by_permission anyway... |
||
967 | * |
||
968 | * @param array|string $codes |
||
969 | * @return SS_List |
||
970 | */ |
||
971 | public function whoIsAllowedAny($codes) { |
||
972 | if (!is_array($codes)) { |
||
973 | $codes = [$codes]; |
||
974 | } |
||
975 | |||
976 | $SQLa_codes = Convert::raw2sql($codes); |
||
977 | $SQL_codes = join("','", $SQLa_codes); |
||
978 | |||
979 | return DataObject::get('Member') |
||
980 | ->where("\"PermissionRoleCode\".\"Code\" IN ('$SQL_codes') OR \"Permission\".\"Code\" IN ('$SQL_codes')") |
||
981 | ->filter("DNProject_Viewers.DNProjectID", $this->ID) |
||
982 | ->leftJoin('Group_Members', "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"") |
||
983 | ->leftJoin('Group', "\"Group_Members\".\"GroupID\" = \"Group\".\"ID\"") |
||
984 | ->leftJoin('DNProject_Viewers', "\"DNProject_Viewers\".\"GroupID\" = \"Group\".\"ID\"") |
||
985 | ->leftJoin('Permission', "\"Permission\".\"GroupID\" = \"Group\".\"ID\"") |
||
986 | ->leftJoin('Group_Roles', "\"Group_Roles\".\"GroupID\" = \"Group\".\"ID\"") |
||
987 | ->leftJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\" = \"PermissionRole\".\"ID\"") |
||
988 | ->leftJoin('PermissionRoleCode', "\"PermissionRoleCode\".\"RoleID\" = \"PermissionRole\".\"ID\""); |
||
989 | } |
||
990 | |||
991 | /** |
||
992 | * Convenience wrapper for a single permission code. |
||
993 | * |
||
994 | * @param string $code |
||
995 | * @param Member|null $member |
||
996 | * |
||
997 | * @return bool |
||
998 | */ |
||
999 | public function allowed($code, $member = null) { |
||
1000 | return $this->allowedAny([$code], $member); |
||
1001 | } |
||
1002 | |||
1003 | /** |
||
1004 | * Checks if a group is allowed to the project and the permission code |
||
1005 | * |
||
1006 | * @param string $permissionCode |
||
1007 | * @param Group $group |
||
1008 | * |
||
1009 | * @return bool |
||
1010 | */ |
||
1011 | public function groupAllowed($permissionCode, Group $group) { |
||
1012 | $viewers = $this->Viewers(); |
||
1013 | if (!$viewers->find('ID', $group->ID)) { |
||
1014 | return false; |
||
1015 | } |
||
1016 | $groups = Permission::get_groups_by_permission($permissionCode); |
||
1017 | if (!$groups->find('ID', $group->ID)) { |
||
1018 | return false; |
||
1019 | } |
||
1020 | return true; |
||
1021 | } |
||
1022 | |||
1023 | /** |
||
1024 | * Check if member has a permission code in this project. |
||
1025 | * |
||
1026 | * @param array|string $codes |
||
1027 | * @param Member|null $member |
||
1028 | * |
||
1029 | * @return bool |
||
1030 | */ |
||
1031 | public function allowedAny($codes, $member = null) { |
||
1032 | if (!$member) { |
||
1033 | $member = Member::currentUser(); |
||
1034 | } |
||
1035 | |||
1036 | if (Permission::checkMember($member, 'ADMIN')) { |
||
1037 | return true; |
||
1038 | } |
||
1039 | |||
1040 | $hits = $this->whoIsAllowedAny($codes)->filter('Member.ID', $member->ID)->count(); |
||
1041 | return ($hits > 0 ? true : false); |
||
1042 | } |
||
1043 | |||
1044 | /** |
||
1045 | * Checks if the environment has been fully built. |
||
1046 | * |
||
1047 | * @return bool |
||
1048 | */ |
||
1049 | public function isProjectReady() { |
||
1050 | if ($this->getRunningInitialEnvironmentCreations()->count() > 0) { |
||
1051 | // We're still creating the initial environments for this project so we're |
||
1052 | // not quite done |
||
1053 | return false; |
||
1054 | } |
||
1055 | |||
1056 | // Provide a hook for further checks. Logic stolen from |
||
1057 | // {@see DataObject::extendedCan()} |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
50% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
1058 | $isDone = $this->extend('isProjectReady'); |
||
1059 | if ($isDone && is_array($isDone)) { |
||
1060 | $isDone = array_filter($isDone, function ($val) { |
||
1061 | return !is_null($val); |
||
1062 | }); |
||
1063 | |||
1064 | // If anything returns false then we're not ready. |
||
1065 | if ($isDone) { |
||
0 ignored issues
–
show
The expression
$isDone of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
1066 | return min($isDone); |
||
1067 | } |
||
1068 | } |
||
1069 | |||
1070 | return true; |
||
1071 | } |
||
1072 | |||
1073 | /** |
||
1074 | * Returns a list of environments still being created. |
||
1075 | * |
||
1076 | * @return SS_List |
||
1077 | */ |
||
1078 | public function getRunningEnvironmentCreations() { |
||
1079 | return $this->CreateEnvironments() |
||
0 ignored issues
–
show
The method
CreateEnvironments() does not exist on DNProject . Did you maybe mean canCreateEnvironments() ?
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. ![]() |
|||
1080 | ->filter('Status', ['Queued', 'Started']); |
||
1081 | } |
||
1082 | |||
1083 | /** |
||
1084 | * Returns a list of initial environments created for this project. |
||
1085 | * |
||
1086 | * @return DataList |
||
1087 | */ |
||
1088 | public function getInitialEnvironmentCreations() { |
||
1089 | return $this->CreateEnvironments()->filter('IsInitialEnvironment', true); |
||
0 ignored issues
–
show
The method
CreateEnvironments() does not exist on DNProject . Did you maybe mean canCreateEnvironments() ?
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. ![]() |
|||
1090 | } |
||
1091 | |||
1092 | /** |
||
1093 | * Only returns initial environments that are being created. |
||
1094 | * |
||
1095 | * @return DataList |
||
1096 | */ |
||
1097 | public function getRunningInitialEnvironmentCreations() { |
||
1098 | return $this->getInitialEnvironmentCreations() |
||
1099 | ->filter('Status', ['Queued', 'Started']); |
||
1100 | } |
||
1101 | |||
1102 | /** |
||
1103 | * Returns a list of completed initial environment creations. This includes failed tasks. |
||
1104 | * |
||
1105 | * @return DataList |
||
1106 | */ |
||
1107 | public function getCompleteInitialEnvironmentCreations() { |
||
1108 | return $this->getInitialEnvironmentCreations() |
||
1109 | ->exclude('Status', ['Queued', 'Started']); |
||
1110 | } |
||
1111 | |||
1112 | /** |
||
1113 | * @param Member $member |
||
1114 | * |
||
1115 | * @return bool |
||
1116 | */ |
||
1117 | public function canCreate($member = null) { |
||
1118 | if (!$member) { |
||
1119 | $member = Member::currentUser(); |
||
1120 | } |
||
1121 | if (!$member) { |
||
1122 | return false; |
||
1123 | } |
||
1124 | |||
1125 | if (Permission::checkMember($member, 'ADMIN')) { |
||
1126 | return true; |
||
1127 | } |
||
1128 | |||
1129 | // This calls canCreate on extensions. |
||
1130 | return parent::canCreate($member); |
||
1131 | } |
||
1132 | |||
1133 | /** |
||
1134 | * This is a proxy call to gitonmy that caches the information per project and sha |
||
1135 | * |
||
1136 | * @param string $sha |
||
1137 | * @return false|\Gitonomy\Git\Commit |
||
1138 | */ |
||
1139 | public function getCommit($sha) { |
||
1140 | $repo = $this->getRepository(); |
||
1141 | if (!$repo) { |
||
1142 | return false; |
||
1143 | } |
||
1144 | |||
1145 | $cachekey = $this->ID . '_commit_' . $sha; |
||
1146 | $cache = self::get_git_cache(); |
||
1147 | if (!($result = $cache->load($cachekey))) { |
||
1148 | try { |
||
1149 | $result = $repo->getCommit($sha); |
||
1150 | } catch (\Gitonomy\Git\Exception\ReferenceNotFoundException $e) { |
||
1151 | return false; |
||
1152 | } |
||
1153 | $cache->save($result, $cachekey, ['gitonomy', 'commit', 'project_' . $this->ID]); |
||
1154 | } |
||
1155 | return $result; |
||
1156 | } |
||
1157 | |||
1158 | /** |
||
1159 | * @param \Gitonomy\Git\Commit $commit |
||
1160 | * @return string |
||
1161 | */ |
||
1162 | View Code Duplication | public function getCommitMessage(\Gitonomy\Git\Commit $commit) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
1163 | $cachekey = $this->ID . '_message_' . $commit->getRevision(); |
||
1164 | $cache = self::get_git_cache(); |
||
1165 | if (!($result = $cache->load($cachekey))) { |
||
1166 | $result = $commit->getMessage(); |
||
1167 | $cache->save($result, $cachekey, ['gitonomy', 'message', 'project_' . $this->ID]); |
||
1168 | } |
||
1169 | return $result; |
||
1170 | } |
||
1171 | |||
1172 | /** |
||
1173 | * get the commit "subject", getCommitMessage get the full message |
||
1174 | * |
||
1175 | * @param \Gitonomy\Git\Commit $commit |
||
1176 | * @return string |
||
1177 | */ |
||
1178 | View Code Duplication | public function getCommitSubjectMessage(\Gitonomy\Git\Commit $commit) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
1179 | $cachekey = $this->ID . '_message_subject' . $commit->getRevision(); |
||
1180 | $cache = self::get_git_cache(); |
||
1181 | if (!($result = $cache->load($cachekey))) { |
||
1182 | $result = $commit->getSubjectMessage(); |
||
1183 | $cache->save($result, $cachekey, ['gitonomy', 'message', 'project_' . $this->ID]); |
||
1184 | } |
||
1185 | return $result; |
||
1186 | } |
||
1187 | |||
1188 | /** |
||
1189 | * @param \Gitonomy\Git\Commit $commit |
||
1190 | * @return mixed |
||
1191 | */ |
||
1192 | public function getCommitTags(\Gitonomy\Git\Commit $commit) { |
||
1193 | $cachekey = $this->ID . '_tags_' . $commit->getRevision(); |
||
1194 | $cache = self::get_git_cache(); |
||
1195 | $result = $cache->load($cachekey); |
||
1196 | // we check against false, because in many cases the tag list is an empty array |
||
1197 | if ($result === false) { |
||
1198 | $repo = $this->getRepository(); |
||
1199 | $result = $repo->getReferences()->resolveTags($commit->getRevision()); |
||
1200 | $cache->save($result, $cachekey, ['gitonomy', 'tags', 'project_' . $this->ID]); |
||
1201 | } |
||
1202 | return $result; |
||
1203 | } |
||
1204 | |||
1205 | /** |
||
1206 | * Setup a gridfield for the environment configs |
||
1207 | * |
||
1208 | * @param FieldList $fields |
||
1209 | * @param GridField $environments |
||
1210 | */ |
||
1211 | protected function setEnvironmentFields(&$fields, $environments) { |
||
1212 | if (!$environments) { |
||
1213 | return; |
||
1214 | } |
||
1215 | |||
1216 | $environments->getConfig()->addComponent(new GridFieldAddNewMultiClass()); |
||
1217 | $environments->getConfig()->removeComponentsByType('GridFieldAddNewButton'); |
||
1218 | $environments->getConfig()->removeComponentsByType('GridFieldAddExistingAutocompleter'); |
||
1219 | $environments->getConfig()->removeComponentsByType('GridFieldDeleteAction'); |
||
1220 | $environments->getConfig()->removeComponentsByType('GridFieldPageCount'); |
||
1221 | if (Config::inst()->get('DNEnvironment', 'allow_web_editing')) { |
||
1222 | $addNewRelease = new GridFieldAddNewButton('toolbar-header-right'); |
||
1223 | $addNewRelease->setButtonName('Add'); |
||
1224 | $environments->getConfig()->addComponent($addNewRelease); |
||
1225 | } |
||
1226 | |||
1227 | $fields->addFieldToTab("Root.Main", $environments); |
||
1228 | } |
||
1229 | |||
1230 | /** |
||
1231 | * @return string |
||
1232 | */ |
||
1233 | protected function getProjectFolderPath() { |
||
1234 | return sprintf('%s/%s', $this->DNData()->getEnvironmentDir(), $this->Name); |
||
1235 | } |
||
1236 | |||
1237 | /** |
||
1238 | * @return ValidationResult |
||
1239 | */ |
||
1240 | protected function validate() { |
||
1241 | $validation = parent::validate(); |
||
1242 | if ($validation->valid()) { |
||
1243 | if (empty($this->Name)) { |
||
1244 | return $validation->error('The stack must have a name.'); |
||
1245 | } |
||
1246 | |||
1247 | // The name is used to build filepaths so should be restricted |
||
1248 | if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\_]+$/', $this->Name)) { |
||
1249 | return $validation->error('Project name can only contain alphanumeric, hyphens and underscores.'); |
||
1250 | } |
||
1251 | |||
1252 | if (empty($this->CVSPath)) { |
||
1253 | return $validation->error('You must provide a repository URL.'); |
||
1254 | } |
||
1255 | |||
1256 | $existing = DNProject::get()->filter('Name', $this->Name); |
||
0 ignored issues
–
show
As per coding style,
self should be used for accessing local static members.
This check looks for accesses to local static members using the fully qualified name instead
of <?php
class Certificate {
const TRIPLEDES_CBC = 'ASDFGHJKL';
private $key;
public function __construct()
{
$this->key = Certificate::TRIPLEDES_CBC;
}
}
While this is perfectly valid, the fully qualified name of ![]() |
|||
1257 | if ($this->ID) { |
||
1258 | $existing = $existing->exclude('ID', $this->ID); |
||
1259 | } |
||
1260 | if ($existing->count() > 0) { |
||
1261 | return $validation->error('A stack already exists with that name.'); |
||
1262 | } |
||
1263 | } |
||
1264 | return $validation; |
||
1265 | } |
||
1266 | |||
1267 | } |
||
1268 | |||
1269 |
This check marks property names that have not been written in camelCase.
In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes
databaseConnectionString
.