OrganizationController::baseVariables()   B
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 0
cts 16
cp 0
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 11
nc 2
nop 1
crap 6
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/organization/license
6
 * @link       https://www.flipboxfactory.com/software/organization/
7
 */
8
9
namespace flipbox\organization\controllers\view;
10
11
use Craft;
12
use craft\base\Field;
13
use craft\helpers\UrlHelper;
14
use flipbox\organization\elements\Organization as OrganizationElement;
15
use flipbox\organization\elements\User as UserElement;
16
use flipbox\organization\events\RegisterOrganizationActions;
17
use flipbox\organization\models\Type;
18
use flipbox\organization\Organization as OrganizationPlugin;
19
use flipbox\organization\web\assets\element\Element;
20
use flipbox\spark\helpers\SiteHelper;
21
use yii\web\Response;
22
23
/**
24
 * @author Flipbox Factory <[email protected]>
25
 * @since 1.0.0
26
 */
27
class OrganizationController extends AbstractController
28
{
29
30
    /** The template base path */
31
    const TEMPLATE_BASE = AbstractController::TEMPLATE_BASE . DIRECTORY_SEPARATOR . 'organization';
32
33
    /**
34
     * @event RegisterOrganizationActionsEvent
35
     */
36
    const EVENT_REGISTER_ORGANIZATION_ACTIONS = 'registerOrganizationActions';
37
38
    /**
39
     * The index view template path
40
     */
41
    const TEMPLATE_INDEX = self::TEMPLATE_BASE . DIRECTORY_SEPARATOR . 'index';
42
43
    /**
44
     * The index view template path
45
     */
46
    const TEMPLATE_UPSERT = self::TEMPLATE_BASE . DIRECTORY_SEPARATOR . 'upsert';
47
48
    /**
49
     * @return string
50
     */
51
    public function actionIndex()
52
    {
53
54
        // Register our asset bundle
55
        Craft::$app->getView()->registerAssetBundle(Element::class);
56
        
57
        // Empty variables for template
58
        $variables = [];
59
60
        // apply base view variables
61
        $this->baseVariables($variables);
62
63
        // (adhere to element index)
64
        $variables['groupHandle'] = '';
65
        $variables['groups'] = $variables['types'];
66
67
        return $this->renderTemplate(
68
            static::TEMPLATE_INDEX,
69
            $variables
70
        );
71
    }
72
73
    /**
74
     * @param null $identifier
75
     * @param null $typeIdentifier
76
     * @param OrganizationElement|null $organization
77
     * @return string
78
     */
79
    public function actionUpsert($identifier = null, $typeIdentifier = null, OrganizationElement $organization = null)
80
    {
81
82
        // Register our asset bundle
83
        Craft::$app->getView()->registerAssetBundle(Element::class);
84
85
        // Empty variables for template
86
        $variables = [];
87
88
        // apply base view variables
89
        $this->baseVariables($variables);
90
91
        if (is_null($organization)) {
92
            if (is_null($identifier)) {
93
                $organization = OrganizationPlugin::getInstance()->getOrganization()->create();
94
            } else {
95
                $organization = OrganizationPlugin::getInstance()->getOrganization()->get($identifier);
96
            }
97
        }
98
99
        if (null !== $typeIdentifier) {
100
            if ($type = OrganizationPlugin::getInstance()->getType()->find($typeIdentifier)) {
101
                $organization->setActiveType($type);
102
            }
103
        }
104
105
        if ($variables['types']) {
106
            $this->getView()->registerJs('new Craft.OrganizationTypeSwitcher();');
107
        }
108
109
        // Template variables
110
        if ($organization->id) {
111
            // Set the "Continue Editing" URL
112
            $variables['continueEditingUrl'] = $variables['baseCpPath'] . '/' . $organization->id;
113
114
            if ($activeType = $organization->getActiveType()) {
115
                $variables['continueEditingUrl'] .= '/' . $activeType->handle;
116
            }
117
            
118
            // Append title
119
            $variables['title'] .= ': ' . Craft::t('organization', 'Edit');
120
121
            // Breadcrumbs
122
            $variables['crumbs'][] = [
123
                'label' => Craft::t(
124
                    'organization',
125
                    "Edit"
126
                ) . ": " . $organization->title,
127
                'url' => UrlHelper::url(
128
                    $variables['baseCpPath'] . '/' . $organization->id
129
                )
130
            ];
131
        } else {
132
            // Set the "Continue Editing" URL
133
            $variables['continueEditingUrl'] = $variables['baseCpPath'] . '/{id}';
134
135
            // Append title
136
            $variables['title'] .= ': ' . Craft::t('organization', 'New');
137
138
            // Breadcrumbs
139
            $variables['crumbs'][] = [
140
                'label' => Craft::t('organization', 'New'),
141
                'url' => UrlHelper::url($variables['baseCpPath'] . '/new')
142
            ];
143
        }
144
145
        $variables['element'] = $organization;
146
147
        $destructiveActions = [];
148
        if (Craft::$app->getUser()->checkPermission('deleteOrganizations')) {
149
            $destructiveActions[] = [
150
                'action' => 'organization/delete',
151
                'label' => Craft::t('app', 'Delete')
152
            ];
153
        }
154
155
        foreach ($organization::statuses() as $statusKey => $statusLabel) {
156
            $variables['statusOptions'][] = [
157
                'label' => Craft::t('site', $statusLabel),
158
                'value' => $statusKey
159
            ];
160
        }
161
162
        // Give plugins a chance to modify these, or add new ones
163
        $event = new RegisterOrganizationActions([
164
            'organization' => $organization,
165
            'destructiveActions' => $destructiveActions,
166
            'miscActions' => [],
167
        ]);
168
        $this->trigger(self::EVENT_REGISTER_ORGANIZATION_ACTIONS, $event);
169
170
        $variables['actions'] = array_filter([
171
            $event->miscActions,
172
            $event->destructiveActions,
173
        ]);
174
175
        $variables['tabs'] = $this->getTabs($organization);
176
177
        // The owner select input criteria
178
        $variables['ownerInputConfiguration'] = $this->getOwnerInputConfiguration($organization);
179
180
        // The user select input criteria
181
        $variables['usersInputConfiguration'] = $this->getUsersInputConfiguration($organization);
182
        $variables['usersIndexConfiguration'] = $this->getUsersIndexConfiguration($organization);
183
184
        return $this->renderTemplate(
185
            static::TEMPLATE_UPSERT,
186
            $variables
187
        );
188
    }
189
190
    /**
191
     * Switches between two entry types.
192
     *
193
     * @return Response
194
     */
195
    public function actionSwitchType(): Response
196
    {
197
198
        $this->requirePostPutPatchRequest();
199
        $this->requireAcceptsJson();
200
201
        $organizationService = OrganizationPlugin::getInstance()->getOrganization();
202
203
        // Optional attributes
204
        $identifier = Craft::$app->getRequest()->getBodyParam('identifier');
205
206
        /** @var OrganizationElement $organizationElement */
207
        if ($identifier) {
208
            $organizationElement = $organizationService->get($identifier);
209
        } else {
210
            $organizationElement = $organizationService->create();
211
        }
212
213
        // Populate
214
        $organizationService->populateFromRequest($organizationElement);
215
216
        // Assemble html (tabs / tab content)
217
        $paneHtml = $this->getView()->renderTemplate(
218
            'organization/_cp/organization/_tabs',
219
            [
220
                    'tabs' => $this->getTabs($organizationElement, false)
221
                ]
222
        ) . $this->getView()->renderTemplate(
223
            'organization/_cp/organization/_fields',
224
            [
225
                    'element' => $organizationElement,
226
                    'fieldLayout' => $organizationElement->getFieldLayout()
227
                ]
228
        );
229
230
        $view = $this->getView();
231
232
        return $this->asJson([
233
            'paneHtml' => $paneHtml,
234
            'headHtml' => $view->getHeadHtml(),
235
            'footHtml' => $view->getBodyHtml(),
236
        ]);
237
    }
238
239
240
    /**
241
     * @param OrganizationElement $organization
242
     * @param bool $includeUsers
243
     * @return array
244
     */
245
    private function getTabs(OrganizationElement $organization, bool $includeUsers = true): array
246
    {
247
248
        $tabs = [];
249
250
        $fieldLayout = $organization->getFieldLayout();
251
252
        $count = 1;
253
        foreach ($fieldLayout->getTabs() as $tab) {
254
            $hasErrors = false;
255
            if ($organization->hasErrors()) {
256
                /** @var Field $field */
257
                foreach ($tab->getFields() as $field) {
258
                    $hasErrors = $organization->getErrors($field->handle) ? true : $hasErrors;
259
                }
260
            }
261
            $tabs[] = [
262
                'label' => $tab->name,
263
                'url' => '#tab' . $count++,
264
                'class' => $hasErrors ? 'error' : null
265
            ];
266
        }
267
268
        if ($organization->getId() && $includeUsers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $organization->getId() of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
269
            $tabs['users'] = [
270
                'label' => Craft::t('organization', 'Users'),
271
                'url' => '#tabusers'
272
            ];
273
        }
274
275
        return $tabs;
276
    }
277
278
    /**
279
     * @param OrganizationElement $organization
280
     * @return array
281
     */
282
    private function getUsersIndexConfiguration(OrganizationElement $organization)
283
    {
284
285
        $selectionCriteria = [
286
            'enabledForSite' => null,
287
            'siteId' => SiteHelper::resolveSiteId($organization->siteId),
288
            'organization' => [
289
                'user' => $organization
290
            ]
291
        ];
292
293
        return [
294
            'elementType' => UserElement::class,
295
            'id' => Craft::$app->getView()->formatInputId('users'),
296
            'source' => 'organization',
297
            'context' => 'index',
298
            'showStatusMenu' => true,
299
            'showSiteMenu' => true,
300
            'sourceElementId' => !empty($organization->id) ? $organization->id : null,
301
            'storageKey' => 'organizationusersindex',
302
            'criteria' => $selectionCriteria
303
        ];
304
    }
305
306
    /**
307
     * @param OrganizationElement $organization
308
     * @return array
309
     */
310
    private function getUsersInputConfiguration(OrganizationElement $organization)
311
    {
312
313
        $selectionCriteria = [
314
            'enabledForSite' => null,
315
            'siteId' => SiteHelper::resolveSiteId($organization->siteId)
316
        ];
317
318
        // Association restrictions
319
        if (OrganizationPlugin::getInstance()->getSettings()->hasAssociationRestriction()) {
320
            $organizationCriteria = [
321
                ':empty:'
322
            ];
323
324
            // Ignore members from the current organization
325
            if ($organization->id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $organization->id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
326
                $organizationCriteria = array_merge(
327
                    [
328
                        'or',
329
                        $organization->id
330
                    ],
331
                    $organizationCriteria
332
                );
333
            }
334
335
            if (OrganizationPlugin::getInstance()->getSettings()->memberAssociationRestriction()) {
336
                $selectionCriteria['organization'] = [
337
                    'member' => $organizationCriteria
338
                ];
339
            } elseif (OrganizationPlugin::getInstance()->getSettings()->userAssociationRestriction()) {
340
                $selectionCriteria['organization'] = [
341
                    'user' => $organizationCriteria
342
                ];
343
            }
344
        }
345
346
        // Disable everyone already associated
347
        $disabledIds = OrganizationPlugin::getInstance()->getOrganization()->getMemberQuery(
348
            $organization,
349
            [
350
                'status' => null
351
            ]
352
        )->ids();
353
354
        return [
355
            'elementType' => UserElement::class,
356
            'id' => Craft::$app->getView()->formatInputId('users'),
357
            'storageKey' => 'organization.users',
358
            'name' => 'users',
359
            'disabledElementIds' => $disabledIds,
360
            'sources' => '*',
361
            'criteria' => $selectionCriteria,
362
            'sourceElementId' => !empty($organization->id) ? $organization->id : null,
363
            'limit' => null,
364
            'viewMode' => 'list',
365
            'selectionLabel' => Craft::t('organization', "Add a user"),
366
            'addAction' => $this->getBaseActionPath() . '/user/associate'
367
        ];
368
    }
369
370
    /**
371
     * @param OrganizationElement $organization
372
     * @return array
373
     */
374
    private function getOwnerInputConfiguration(OrganizationElement $organization)
375
    {
376
377
        $selectionCriteria = [
378
            'enabledForSite' => null,
379
            'siteId' => SiteHelper::resolveSiteId($organization->siteId)
380
        ];
381
382
        // Association restrictions
383
        if (OrganizationPlugin::getInstance()->getSettings()->hasAssociationRestriction() ||
384
            OrganizationPlugin::getInstance()->getSettings()->uniqueOwner
385
        ) {
386
            $organizationCriteria = [
387
                ':empty:'
388
            ];
389
390
            // Ignore members from the current organization
391
            if ($organization->id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $organization->id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
392
                $organizationCriteria = array_merge(
393
                    [
394
                        'or',
395
                        $organization->id
396
                    ],
397
                    $organizationCriteria
398
                );
399
            }
400
401
            // Association restrictions
402
            if (OrganizationPlugin::getInstance()->getSettings()->hasAssociationRestriction()) {
403
                $selectionCriteria['organization'] = [
404
                    'member' => $organizationCriteria
405
                ];
406
            } else {
407
                $selectionCriteria['organization'] = [
408
                    'owner' => $organizationCriteria
409
                ];
410
            }
411
        }
412
413
        // Disable everyone already associated
414
        $disabledIds = OrganizationPlugin::getInstance()->getOrganization()->getUserQuery(
415
            $organization,
416
            [
417
                'status' => null
418
            ]
419
        )->ids();
420
421
        return [
422
            'label' => Craft::t('organization', "Owner"),
423
            'elementType' => UserElement::class,
424
            'id' => Craft::$app->getView()->formatInputId('owner'),
425
            'fieldId' => 'ownerId',
426
            'storageKey' => 'organization.owner',
427
            'name' => 'owner',
428
            'disabledElementIds' => $disabledIds,
429
            'elements' => [$organization->getOwner()],
430
            'sources' => '*',
431
            'criteria' => $selectionCriteria,
432
            'sourceElementId' => !empty($organization->id) ? $organization->id : null,
433
            'limit' => 1,
434
            'required' => OrganizationPlugin::getInstance()->getSettings()->requireOwner,
435
            'viewMode' => 'list',
436
            'selectionLabel' => Craft::t('organization', "Add an owner"),
437
            'errors' => $organization->getErrors('ownerId')
438
        ];
439
    }
440
441
442
    /**
443
     * Set base variables used to generate template views
444
     *
445
     * @param array $variables
446
     */
447
    protected function baseVariables(array &$variables = [])
448
    {
449
450
        // Get base variables
451
        parent::baseVariables($variables);
452
453
        // Find all organization types
454
        $variables['types'] = OrganizationPlugin::getInstance()->getType()->findAll();
455
456
        // Create organization types option array
457
        $variables['typeOptions'] = [];
458
        /** @var Type $type */
459
        foreach ($variables['types'] as $type) {
460
            $variables['typeOptions'][] = [
461
                'label' => Craft::t('site', $type->name),
462
                'value' => $type->id
463
            ];
464
        }
465
466
        // Breadcrumbs
467
        $variables['crumbs'][] = [
468
            'label' => $variables['title'],
469
            'url' => UrlHelper::url($variables['baseCpPath'])
470
        ];
471
    }
472
}
473