Completed
Push — master ( 89f62d...6e0e08 )
by Nicola
02:25
created

code/CarouselPage.php (1 issue)

Upgrade to new PHP Analysis Engine

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
 * Provide additional methods specially crafted for carousels.
5
 *
6
 * @package silverstripe-carousel
7
 */
8
class CarouselImageExtension extends DataExtension
9
{
10
    /**
11
     * Generate a scaled image suitable for a carousel.
12
     *
13
     * If $width and $height are greater than 0, it is equivalent to
14
     * Image_Backend::croppedResize().
15
     *
16
     * If only $width is greater than 0, it is equivalent to SetWidth().
17
     *
18
     * If only $height is greater than 0, it is equivalent to
19
     * SetHeight().
20
     *
21
     * If neither $width or $height are greater than 0, return the
22
     * original image.
23
     *
24
     * @param  integer $width   The width to set or 0.
25
     * @param  integer $height  The height to set or 0.
26
     * @return Image_Backend
27
     */
28
    public function MaybeCroppedImage($width, $height)
29
    {
30
        return $this->owner->getFormattedImage('MaybeCroppedImage', $width, $height);
31
    }
32
33
    /**
34
     * Low level function for CarouselImageExtension::MaybeCroppedImage().
35
     *
36
     * @param  Image_Backend $backend
37
     * @param  integer $width
38
     * @param  integer $height
39
     * @return Image_Backend
40
     */
41
    public function generateMaybeCroppedImage(Image_Backend $backend, $width, $height)
42
    {
43
        if ($width > 0 && $height > 0) {
44
            return $backend->croppedResize($width, $height);
45
        } elseif ($width > 0) {
46
            return $backend->resizeByWidth($width);
47
        } elseif ($height > 0) {
48
            return $backend->resizeByHeight($height);
49
        } else {
50
            return $backend;
51
        }
52
    }
53
}
54
55
/**
56
 * This class is needed in order to handle the 'Content' field of the
57
 * 'File' table with the WYSIWYG editor. That field is TEXT, hence just
58
 * using HtmlEditorField will result in an error when the 'saveInto'
59
 * method is called.
60
 *
61
 * @package silverstripe-carousel
62
 */
63
class CarouselCaptionField extends HtmlEditorField
64
{
65
    /**
66
     * Override the default constructor to have saner settings.
67
     *
68
     * @param string $name  The internal field name, passed to forms.
69
     * @param string $title The human-readable field label.
0 ignored issues
show
Should the type for parameter $title not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
70
     * @param mixed  $value The value of the field.
71
     */
72
    public function __construct($name, $title = null, $value = '')
73
    {
74
        parent::__construct($name, $title, $value);
75
        $this->rows = 5;
76
        // The .htmleditor class enables TinyMCE
77
        $this->addExtraClass('htmleditor');
78
    }
79
80
    /**
81
     * Implementation directly borrowed from HtmlEditorField
82
     * without the blocking or useless code.
83
     *
84
     * @param DataObjectInterface $record
85
     */
86
    public function saveInto(DataObjectInterface $record)
87
    {
88
        $htmlValue = Injector::inst()->create('HTMLValue', $this->value);
89
90
        // Sanitise if requested
91
        if ($this->config()->sanitise_server_side) {
92
            $santiser = Injector::inst()->create('HtmlEditorSanitiser', HtmlEditorConfig::get_active());
93
            $santiser->sanitise($htmlValue);
94
        }
95
96
        $this->extend('processHTML', $htmlValue);
97
        $record->{$this->name} = $htmlValue->getContent();
98
    }
99
}
100
101
/**
102
 * Basic page type owning a carousel.
103
 *
104
 * @package silverstripe-carousel
105
 */
106
class CarouselPage extends Page
107
{
108
    private static $icon = 'carousel/img/carousel.png';
109
110
    private static $db = array(
111
        'Captions' => 'Boolean',
112
        'Width'    => 'Int',
113
        'Height'   => 'Int',
114
    );
115
116
    private static $many_many = array(
117
        'Images'   => 'Image',
118
    );
119
120
    private static $many_many_extraFields = array(
121
        'Images'   => array(
122
            'SortOrder' => 'Int',
123
        ),
124
    );
125
126
    /**
127
     * Search the first class name (that must have a 'Page' suffix) in
128
     * the object hierarchy that has a correspoding folder in
129
     * ASSETS_PATH, that is a folder with the same name with the 'Page'
130
     * suffix stripped out. This folder will be returned and used as
131
     * custom folder in the upload field.
132
     *
133
     * For example, if this class is `HomePage` and it is inherited from
134
     * `CarouselPage`, this function will check for `Home` first and
135
     * `Carousel` after.
136
     *
137
     * If no valid folders are found, `false` is returned.
138
     *
139
     * @return string|false
140
     */
141
    protected function getClassFolder()
142
    {
143
        for ($class = $this->class; $class; $class = get_parent_class($class)) {
144
            $folder = preg_replace('/Page$/', '', $class);
145
            if ($folder != $class && is_dir(ASSETS_PATH . '/' . $folder)) {
146
                return $folder;
147
            }
148
        }
149
150
        // Why false? Because false is the proper value to set in
151
        // setFolderName() to get the default folder (i.e. 'Uploads').
152
        return false;
153
    }
154
155
    /**
156
     * Add the "Images" tab to the content form of the page.
157
     *
158
     * The images are linked to the page with a many-many relationship,
159
     * so if an image is shared among different carousels there is no
160
     * need to upload it multiple times.
161
     *
162
     * @return FieldList
163
     */
164
    public function getCMSFields()
165
    {
166
        $fields = parent::getCMSFields();
167
168
        $field = SortableUploadField::create('Images', _t('CarouselPage.db_Images'));
169
        $field->setFolderName($this->getClassFolder());
170
171
        // Enable HTML caption handling if captions are enabled
172
        if ($this->Captions) {
173
            $caption = CarouselCaptionField::create('Content', _t('CarouselPage.Caption'));
174
            $field->setFileEditFields(FieldList::create($caption));
175
            unset($caption);
176
        }
177
178
        $root = $fields->fieldByName('Root');
179
        $tab = $root->fieldByName('Images');
180
        if (! $tab) {
181
            $tab = Tab::create('Images');
182
            $tab->setTitle(_t('CarouselPage.db_Images'));
183
            $root->insertAfter($tab, 'Main');
184
        }
185
        $tab->push($field);
186
187
        return $fields;
188
    }
189
190
    /**
191
     * Add carousel related fields to the page settings.
192
     *
193
     * Every CarouselPage instance can have its own settings, that is
194
     * different pages can own carousels of different sizes.
195
     *
196
     * @return FieldList
197
     */
198
    public function getSettingsFields()
199
    {
200
        $fields = parent::getSettingsFields();
201
202
        $settings = FieldGroup::create(
203
            FieldGroup::create(
204
                NumericField::create('Width', _t('CarouselPage.db_Width')),
205
                NumericField::create('Height', _t('CarouselPage.db_Height')),
206
                CheckboxField::create('Captions', _t('CarouselPage.db_Captions'))
207
            )
208
        );
209
        $settings->setName('Carousel');
210
        $settings->setTitle(_t('CarouselPage.SINGULARNAME'));
211
        $fields->addFieldToTab('Root.Settings', $settings);
212
213
        return $fields;
214
    }
215
216
    /**
217
     * Ensure ThumbnailWidth and ThumbnailHeight are valorized.
218
     *
219
     * Although width and height for the images in the carousel can be
220
     * omitted (see CarouselImageExtension::MaybeCroppedImage() for
221
     * algorithm details) the thumbnail extents must be defined.
222
     *
223
     * @return Validator
224
     */
225
    public function getCMSValidator()
226
    {
227
        return RequiredFields::create(
228
            'ThumbnailWidth',
229
            'ThumbnailHeight'
230
        );
231
    }
232
233
    /**
234
     * Out of the box support for silverstripe/silverstripe-translatable.
235
     *
236
     * Duplicate the image list whenever a new translation is created.
237
     * It the translatable module is not used, this will simply be a
238
     * dead method.
239
     *
240
     * @param boolean $save Whether the new page should be saved to the
241
     *                      database.
242
     */
243
    public function onTranslatableCreate($save)
244
    {
245
        // Chain up the parent method, if it exists
246
        if (method_exists('Page', 'onTranslatableCreate')) {
247
            parent::onTranslatableCreate($save);
248
        }
249
250
        $master = $this->getTranslation(Translatable::default_locale());
251
252
        foreach ($master->Images() as $master_image) {
253
            $image = $master_image->duplicate($save);
254
            $this->Images()->add($image);
255
        }
256
    }
257
}
258
259
/**
260
 * Controller for CarouselPage.
261
 *
262
 * @package silverstripe-carousel
263
 */
264
class CarouselPage_Controller extends Page_Controller
265
{
266
    /**
267
     * From the controller the images are returned in proper order.
268
     * This means `<% loop $Images %>` returns the expected result.
269
     */
270
    public function Images()
271
    {
272
        return $this->dataRecord->Images()->Sort('SortOrder');
273
    }
274
}
275