Issues (26)

src/Admin/DashboardWelcomeQuicklinks.php (16 issues)

1
<?php
2
3
namespace Sunnysideup\DashboardWelcomeQuicklinks\Admin;
4
5
use SilverStripe\Admin\LeftAndMain;
0 ignored issues
show
The type SilverStripe\Admin\LeftAndMain was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use SilverStripe\Core\ClassInfo;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Forms\LiteralField;
10
use SilverStripe\ORM\ArrayList;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\SiteConfig\SiteConfig;
0 ignored issues
show
The type SilverStripe\SiteConfig\SiteConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use SilverStripe\View\ArrayData;
14
use Sunnysideup\DashboardWelcomeQuicklinks\Api\DefaultDashboardProvider;
15
use Sunnysideup\DashboardWelcomeQuicklinks\Interfaces\DashboardWelcomeQuicklinksProvider;
16
17
/**
18
 * Class \Sunnysideup\DashboardWelcomeQuicklinks\Admin\DashboardWelcomeQuicklinks
19
 *
20
 */
21
class DashboardWelcomeQuicklinks extends LeftAndMain
22
{
23
    protected static int $item_counter = 0;
24
25
    protected static int $group_counter = 0;
26
27
    protected static array $links = [];
28
29
30
    public static function add_group(string $groupCode, string $title, ?int $sort = 0)
31
    {
32
        self::$group_counter++;
33
34
        self::$links[$groupCode] = [
35
            'Title' => $title,
36
            'SortOrder' => $sort ?: self::$group_counter,
37
        ];
38
    }
39
40
    public static function add_link(string $groupCode, string $title, string $link, ?array $insideLink = [])
41
    {
42
        self::$item_counter++;
43
        if (array_key_exists(0, $insideLink) && array_key_exists(1, $insideLink)) {
44
            $keys = ['Title', 'Link'];
45
            $insideLink = array_combine($keys, $insideLink);
46
        }
47
        self::$links[$groupCode]['Items'][] = [
48
            'Title' => $title,
49
            'Link' => $link,
50
            'InsideLink' => $insideLink,
51
        ];
52
    }
53
54
    public static function get_links()
55
    {
56
        return self::$links;
57
    }
58
59
    public static function get_base_phrase(string $phrase): string
60
    {
61
        if (! in_array($phrase, ['add', 'review', 'edit', 'more'])) {
62
            user_error('Phrase must be one of "add", "review", or "edit"', E_USER_ERROR);
63
        }
64
        $phrase = Config::inst()->get(static::class, $phrase . '_phrase');
65
        return _t('DashboardWelcomeQuicklinks.' . $phrase, $phrase);
66
    }
67
68
    private static string $add_phrase = '+';
0 ignored issues
show
The private property $add_phrase is not used, and could be removed.
Loading history...
69
70
    private static string $review_phrase = '☑';
0 ignored issues
show
The private property $review_phrase is not used, and could be removed.
Loading history...
71
72
    private static string $edit_phrase = '✎';
0 ignored issues
show
The private property $edit_phrase is not used, and could be removed.
Loading history...
73
74
    private static string $url_segment = 'go';
0 ignored issues
show
The private property $url_segment is not used, and could be removed.
Loading history...
75
76
    private static $more_phrase = '&raquo;';
0 ignored issues
show
The private property $more_phrase is not used, and could be removed.
Loading history...
77
78
    private static bool $use_default_dashboard_provider = true;
0 ignored issues
show
The private property $use_default_dashboard_provider is not used, and could be removed.
Loading history...
79
80
    private static $menu_title = 'Quick-links';
0 ignored issues
show
The private property $menu_title is not used, and could be removed.
Loading history...
81
82
    private static $menu_icon_class = 'font-icon-dashboard';
0 ignored issues
show
The private property $menu_icon_class is not used, and could be removed.
Loading history...
83
84
    private static $menu_priority = 99999;
0 ignored issues
show
The private property $menu_priority is not used, and could be removed.
Loading history...
85
86
    private static $colour_options = [];
0 ignored issues
show
The private property $colour_options is not used, and could be removed.
Loading history...
87
88
    private static $max_shortcuts_per_group = 7;
0 ignored issues
show
The private property $max_shortcuts_per_group is not used, and could be removed.
Loading history...
89
90
    private static $default_colour_options = [
0 ignored issues
show
The private property $default_colour_options is not used, and could be removed.
Loading history...
91
        '#0D47A1',
92
        '#01579B',
93
        '#006064',
94
        '#004D40',
95
        '#1B5E20',
96
        '#33691E',
97
        '#827717',
98
        '#F57F17',
99
        '#FF6F00',
100
        '#E65100',
101
        '#BF360C',
102
        '#3E2723',
103
        '#212121',
104
        '#B71C1C',
105
        '#880E4F',
106
        '#4A148C',
107
        '#311B92',
108
        '#1A237E',
109
    ];
110
111
    /**
112
     * easy to distinguish colours
113
     *
114
     * @var array
115
     */
116
    private static $default_colour_options1 = [
0 ignored issues
show
The private property $default_colour_options1 is not used, and could be removed.
Loading history...
117
        '#F2F3F4',
118
        '#222222',
119
        '#F3C300',
120
        '#875692',
121
        '#F38400',
122
        '#A1CAF1',
123
        '#BE0032',
124
        '#C2B280',
125
        '#848482',
126
        '#008856',
127
        '#E68FAC',
128
        '#0067A5',
129
        '#F99379',
130
        '#604E97',
131
        '#F6A600',
132
        '#B3446C',
133
        '#DCD300',
134
        '#882D17',
135
        '#8DB600',
136
        '#654522',
137
        '#E25822',
138
        '#2B3D26',
139
    ];
140
141
    /**
142
     * light colours
143
     *
144
     * @var array
145
     */
146
    private static $default_colour_options3 = [
0 ignored issues
show
The private property $default_colour_options3 is not used, and could be removed.
Loading history...
147
        '#FFEBEE',
148
        '#FCE4EC',
149
        '#F3E5F5',
150
        '#EDE7F6',
151
        '#E8EAF6',
152
        '#E3F2FD',
153
        '#E1F5FE',
154
        '#E0F7FA',
155
        '#E0F2F1',
156
        '#E8F5E9',
157
        '#F1F8E9',
158
        '#F9FBE7',
159
        '#FFFDE7',
160
        '#FFF8E1',
161
        '#FFF3E0',
162
        '#FBE9E7',
163
        '#EFEBE9',
164
        '#FAFAFA',
165
    ];
166
167
    public function getEditForm($id = null, $fields = null)
168
    {
169
        $form = parent::getEditForm($id, $fields);
170
171
        // if ($form instanceof HTTPResponse) {
172
        //     return $form;
173
        // }
174
175
        $this->updateFormWithQuicklinks($form);
176
177
        return $form;
178
    }
179
180
    public function updateFormWithQuicklinks($form)
181
    {
182
        $shortcuts = $this->getLinksFromImplementor();
183
        // print_r($shortcuts);
184
        $html = '';
185
        $max = $this->Config()->get('max_shortcuts_per_group');
186
        if (count($shortcuts) > 0) {
187
            $html = '<div class="grid-wrapper">';
188
189
            usort(
190
                $shortcuts,
191
                function ($a, $b) {
192
                    $b['SortOrder'] ?? 0;
193
                    $a['SortOrder'] ?? 0;
194
                }
195
            );
196
197
            foreach ($shortcuts as $groupCode => $groupDetails) {
198
                $colour = '';
199
                if (! empty($groupDetails['Colour'])) {
200
                    $colour = 'style="background-color: ' . $groupDetails['Colour'] . '"';
201
                }
202
                $icon = '';
203
                if (! empty($groupDetails['IconClass'])) {
204
                    $icon = '<i class="' . $groupDetails['IconClass'] . '"></i> ';
205
                }
206
                $html .= '
207
                <div class="grid-cell" ' . $colour . '>
208
                    <div class="header">
209
                    <h1>' . $icon . '' . ($groupDetails['Title'] ?? $groupCode) . '</h1>
210
                    </div>
211
                    <div class="entries">';
212
                $items = $groupDetails['Items'] ?? [];
213
                foreach ($items as $pos => $entry) {
214
                    if (! empty($entry['Link']) && class_exists($entry['Link'])) {
215
                        $obj = Injector::inst()->get($entry['Link']);
216
                        if ($obj instanceof DataObject) {
217
                            $entry['Link'] = DataObject::get_one($entry['Link'])->CMSEditLink();
218
                        } else {
219
                            $entry['Link'] = $obj->Link();
220
                        }
221
                    }
222
                    $html .= $this->createInnerLink($entry, $pos, $items, $max);
223
                }
224
                $html .= '</div></div>';
225
            }
226
            $html .= '</div>';
227
        } else {
228
            $html .= '<p>Please start editing by making a selection from the left.</p>';
229
        }
230
        $kc = (array) $this->Config()->get('colour_options');
231
        if ($kc === []) {
232
            $kc = $this->Config()->get('default_colour_options');
233
        }
234
        $kcCount = count($kc);
235
        $colours = '';
236
        foreach ($kc as $key => $colour) {
237
            $colours .= ' .grid-wrapper .grid-cell:nth-child(' . $kcCount . 'n+' . ($key + 1) . ') div.header {background-color: ' . $colour . '; color: ' . $this->getFontColor($colour) . '!important;}';
238
        }
239
        $html .= '<script>window.setTimeout(dashboardWelcomeQuicklinksSetupInputAndFilter, 500)</script>';
240
        $html .= '<style>' . $colours . '</style>';
241
        $form->Fields()->push(LiteralField::create('ShortCuts', $html));
242
    }
243
244
    protected function createInnerLink($entry, $pos, $items, $max)
245
    {
246
        $html = '';
247
        $entry['Class'] = $entry['Class'] ?? '';
248
        $entry['Class'] .= ($pos > $max) ? ' more-item' : '';
249
        $html .= $this->makeShortCut($entry)->Field();
250
        if ($pos > $max && count($items) == $pos + 1) {
251
            $html .= $this->makeShortCut(
252
                [
253
                    'Title' => DashboardWelcomeQuicklinks::get_base_phrase('more'),
254
                    'Link' => '#',
255
                    'OnClick' => 'dashboardWelcomeQuicklinksSetupInputAndFilterToggleMore(event); return false;',
256
                    'Class' => 'more-item-more',
257
                ]
258
            )->Field();
259
        }
260
        return $html;
261
    }
262
263
    protected function getLinksFromImplementor()
264
    {
265
        $array = [];
266
        $useDefaultDashboard = (bool) $this->config()->get('use_default_dashboard_provider');
267
        $classNames = ClassInfo::implementorsOf(DashboardWelcomeQuicklinksProvider::class);
268
        foreach ($classNames as $className) {
269
            if ($useDefaultDashboard === false && (string) $className === DefaultDashboardProvider::class) {
270
                continue;
271
            }
272
            $array += Injector::inst()->get($className)->provideDashboardWelcomeQuicklinks();
273
        }
274
        return $array;
275
    }
276
277
    protected function makeShortCut(array $entry, ?bool $isInsideLink = false): LiteralField|string
278
    {
279
        $title = (string) $entry['Title'];
280
        $link = (string) $entry['Link'];
281
        $onclick = (string) ($entry['OnClick'] ?? '');
282
        $script = (string) ($entry['Script'] ?? '');
283
        $class = (string) ($entry['Class'] ?? '');
284
        $iconClass = (string) ($entry['IconClass'] ?? '');
285
        $target = (string) ($entry['Target'] ?? '');
286
        $insideLink = (array) ($entry['InsideLink'] ?? []);
287
288
        $name = preg_replace('#[\W_]+#u', '', $title);
289
        $html = '';
290
        if ($onclick !== '' && $onclick !== '0') {
291
            $onclick = ' onclick="' . $onclick . '"';
292
        }
293
        if ($script !== '' && $script !== '0') {
294
            $script = '<script>' . $script . '</script>';
295
        }
296
        $icon = '';
297
        if ($iconClass !== null && $iconClass !== '' && $iconClass !== '0') {
298
            $icon = '<i class="' . $iconClass . '"></i> ';
299
        }
300
        if ($target === '' || $target === '0') {
301
            $target = '_self';
302
        }
303
        $tag = $isInsideLink ? 'span' : 'h2';
304
        if ($class !== '' && $class !== '0') {
305
            $class = ' class="' . $class . '"';
306
        }
307
        $insideLinkHTML = '';
308
        if ($insideLink !== []) {
309
            $insideLink['Class'] = ($insideLink['Class'] ?? '') . ' inside-link';
310
            $insideLinkHTML = $this->makeShortCut($insideLink, true);
311
        }
312
        $target = ' target="' . $target . '"';
313
        if ($link !== '' && $link !== '0') {
314
            $html = '' . $script . '<' . $tag . '' . $class . '>' . $icon . '<a href="' . $link . '" ' . $target . ' ' . $onclick . '>' . $title . '</a>' . $insideLinkHTML . '</' . $tag . '>';
315
        } else {
316
            $html = '' . $script . '<' . $tag . '' . $class . '>' . $title . '' . $insideLinkHTML . '</' . $tag . '>
317
            ';
318
        }
319
        $html = preg_replace('/\s+/', ' ', $html);
320
        if ($isInsideLink) {
321
            return $html;
322
        } else {
323
            return LiteralField::create($name, $html);
324
        }
325
    }
326
327
    /**
328
     * @return string
329
     */
330
    public function Title()
331
    {
332
        $app = $this->getApplicationName();
333
        $siteConfigTitle = SiteConfig::current_site_config()->Title;
334
        if ($siteConfigTitle) {
335
            $app = $siteConfigTitle . ' (' . $app . ')';
336
        }
337
        return ($section = $this->SectionTitle()) ? sprintf('%s for %s', $section, $app) : $app;
338
    }
339
340
    /**
341
     * @param bool $unlinked
342
     * @return ArrayList<ArrayData>
343
     */
344
    public function Breadcrumbs($unlinked = false)
345
    {
346
        return new ArrayList([
347
            new ArrayData([
348
                'Title' => $this->Title(),
349
                'Link' => ($unlinked) ? false : $this->Link(),
350
            ]),
351
        ]);
352
    }
353
354
    protected function getFontColor(string $backgroundColor): string
355
    {
356
        // Convert hex color to RGB
357
        $r = hexdec(substr($backgroundColor, 1, 2));
358
        $g = hexdec(substr($backgroundColor, 3, 2));
359
        $b = hexdec(substr($backgroundColor, 5, 2));
360
361
        // Calculate luminance
362
        $luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255;
363
364
        // If luminance is greater than 0.5, use black font; otherwise, use white
365
        return $luminance > 0.5 ? '#222' : '#fff';
366
    }
367
}
368