Completed
Branch master (e8947e)
by Andreas
15:09
created

midcom_helper_datamanager2_widget_images   D

Complexity

Total Complexity 78

Size/Duplication

Total Lines 691
Duplicated Lines 24.02 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 166
loc 691
rs 4.7579
c 0
b 0
f 0
wmc 78
lcom 1
cbo 11

18 Methods

Rating   Name   Duplication   Size   Complexity  
B _on_initialize() 17 32 4
A _get_filename_validation_script() 0 19 1
B _add_table_header() 34 34 3
B _add_new_upload_row_old() 20 63 5
B _add_new_upload_row() 18 41 5
C _add_image_row() 12 73 8
B _get_preview_html() 0 43 6
B _add_controls() 9 36 3
A _add_table_footer() 0 6 1
B add_elements_to_form() 7 18 6
A _compute_elements() 21 21 3
C _check_new_upload() 4 48 8
C _check_for_update() 4 36 8
A on_submit() 20 20 3
A freeze() 0 6 1
A unfreeze() 0 6 1
C sync_type_with_widget() 0 38 8
A get_default() 0 20 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like midcom_helper_datamanager2_widget_images often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use midcom_helper_datamanager2_widget_images, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package midcom.helper.datamanager2
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * Datamanager 2 images widget
11
 *
12
 * As with all subclasses, the actual initialization is done in the initialize() function.
13
 *
14
 * This widget supports the images type or any subtype thereof.
15
 *
16
 * All processing is done during the on_submit handlers, enforcing immediate update of the
17
 * associated storage objects. No other solution is possible, since we need to transfer
18
 * uploaded files somehow through multiple requests.
19
 *
20
 * The type will show a tabular view of all uploaded images. Existing images have
21
 * an editable title and can be deleted or replaced. A single new upload line is displayed
22
 * always. There is no preview, but there is a download link.
23
 *
24
 * <b>Available configuration options:</b>
25
 *
26
 * - set_name_and_title_on_upload use this if you want the user to be able to set the
27
 *   filename and title when uploading a form.
28
 * - integer max_count Maximum number of images allowed for a field. Set this
29
 *
30
 * <b>Implementation notes:</b>
31
 *
32
 * The construction of the widget is relatively complex, it relies on a combination of
33
 * static and input elements to do its work. It should be fairly customizable using CSS.
34
 *
35
 * 1. Quickform Element Naming
36
 *
37
 * All elements will be added in a group using the groupname[elementname] Feature of QF.
38
 * Static elements are all prefixed s_, f.x. s_header. The actual elements use an e_, f.x.
39
 * e_new_title. All elements in the new upload row append a new_ to this prefix as seen
40
 * in the last example. Finally, elements referencing existing attachments append an
41
 * exist_{$identifier}_ to the prefix, f.x. e_exist_{$identifier}_title.
42
 *
43
 * 2. CSS names
44
 *
45
 * The table gets the Name of the field as id and midcom_helper_datamanager2_widget_images
46
 * as class. Each column also gets its own CSS class: filename, title, file, upload and delete.
47
 * An additional class is assigned depending whether this is a row for an existing item (exist) or
48
 * a new one (new). So a full class for the new filename element would be "new filename". Note,
49
 * that the classes are assigned to both the td and input elements. The th elements do not have
50
 * any additional class
51
 *
52
 * 3. Image identifiers
53
 *
54
 * The auto-generated image identifiers from the images base type are used.
55
 *
56
 * @package midcom.helper.datamanager2
57
 */
58
class midcom_helper_datamanager2_widget_images extends midcom_helper_datamanager2_widget
1 ignored issue
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
59
{
60
    /**
61
     * The group of elements forming our widget.
62
     *
63
     * @var HTML_QuickForm_Group
64
     */
65
    private $_group = null;
66
67
    /**
68
     * The list of elements added to the widget, indexed by their element name.
69
     *
70
     * @var Array
71
     */
72
    private $_elements = null;
73
74
    /**
75
     * Should the user be able to set the filename and title on upload?
76
     * If so, set this to true.
77
     *
78
     * @var boolean
79
     */
80
    var $set_name_and_title_on_upload = true;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $set_name_and_title_on_upload.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
81
82
    /**
83
     * Maximum amount of images allowed to be stored in the same field
84
     *
85
     * @var integer
86
     */
87
    public $max_count = 0;
88
89
    /**
90
     * Sort index or what is the position in the list
91
     *
92
     * @var integer
93
     */
94
    private $_sort_index = 1;
95
96
    /**
97
     * The initialization event handler post-processes the maxlength setting.
98
     */
99
    public function _on_initialize()
100
    {
101
        $this->_require_type_class('midcom_helper_datamanager2_type_images');
102
103
        // Reflect the type config setting for maximum count
104
        if (   isset($this->_type->max_count)
105
            && !$this->max_count)
106
        {
107
            $this->max_count = $this->_type->max_count;
108
        }
109
110
        // Create sortable
111 View Code Duplication
        if ($this->_type->sortable)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
112
        {
113
            midcom::get()->head->enable_jquery();
114
            midcom::get()->head->add_jsfile(MIDCOM_STATIC_URL . '/midcom.helper.datamanager2/datamanager2.tablesorter.js');
115
            // Configuration options
116
            midcom::get()->head->add_jscript("
117
                jQuery(document).ready(function()
118
                {
119
                    jQuery('#{$this->_namespace}{$this->name}')
120
                        .create_tablesorter({
121
                            max_count: 0,
122
                            sortable_rows: true,
123
                            allow_delete: false
124
                        });
125
                });
126
            ");
127
        }
128
129
        midcom::get()->head->add_jscript($this->_get_filename_validation_script());
130
    }
131
132
    private function _get_filename_validation_script()
133
    {
134
        return <<<END
135
function midcom_helper_dm2_widget_images_check(evt, id) {
136
    evt = (evt) ? evt: ( (window.event) ? event : null);
137
    var obj,reg, msg;
138
    if (evt) {
139
        reg = /\.(png|gif|jpe?g|tiff?)$/;
140
        obj = (evt.target) ? evt.target : evt.srcElement;
141
        if (!obj.value.match(reg)) {
142
            obj.style.color = "red";
143
            msg = document.getElementById(id);
144
            msg.style.display = "block";
145
        }
146
    }
147
}
148
149
END;
150
    }
151
152
    /**
153
     * Adds the table header to the widget.
154
     *
155
     * @param boolean $frozen Set this to true, if you want to skip all elements which cannot be frozen.
156
     */
157 View Code Duplication
    private function _add_table_header($frozen)
0 ignored issues
show
Duplication introduced by
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.

Loading history...
158
    {
159
        if ($frozen)
160
        {
161
            $html = "<table class=\"midcom_helper_datamanager2_widget_images\" id=\"{$this->_namespace}{$this->name}\" >\n
162
                        <thead>\n
163
                            <tr>\n
164
                                <th class=\"filename\">" . $this->_l10n_midcom->get('name') . "</th>\n
165
                                <th class=\"title\">" . $this->_l10n_midcom->get('title') . "</th>\n
166
                            </tr>\n
167
                        </thead>\n
168
                        <tbody>\n";
169
        }
170
        else
171
        {
172
            $index = '';
173
            if ($this->_type->sortable)
174
            {
175
                $index = "            <th class=\"index\">" . $this->_l10n->get('index') . "</th>\n";
176
            }
177
178
            $html = "<table class=\"midcom_helper_datamanager2_widget_downloads\" id=\"{$this->_namespace}{$this->name}\" >\n
179
                        <thead>\n
180
                            <tr>\n" .
181
                    $index .
182
                    "            <th class=\"filename\">" . $this->_l10n_midcom->get('name') . "</th>\n
183
                                 <th class=\"title\">" . $this->_l10n_midcom->get('title') . "</th>\n
184
                                 <th class=\"upload\">" . $this->_l10n_midcom->get('upload') . "</th>\n
185
                             </tr>\n
186
                         </thead>\n
187
                        <tbody>\n";
188
        }
189
        $this->_elements['s_header'] = $this->_form->createElement('static', 's_header', '', $html);
190
    }
191
192
    /**
193
     * Adds the new upload row to the bottom of the table.
194
     *
195
     * @param boolean $frozen Set this to true, if you want to skip all elements which cannot be frozen.
196
     */
197
    private function _add_new_upload_row_old($frozen)
198
    {
199
        // Show only a configured amount of new image rows
200
        if (   $this->max_count
201
            && count($this->_type->images) >= $this->max_count)
202
        {
203
            return;
204
        }
205
206
        $sortable = '';
207
        if ($this->_type->sortable)
208
        {
209
            $sortable = "            <td class=\"sortable_new\"></td>\n";
210
        }
211
212
        // Filename column
213
        $html = "         <tr>\n" .
214
                $sortable .
215
                "            <td class=\"new filename\">";
216
        $this->_elements['s_new_filename'] = $this->_form->createElement('static', 's_new_filename', '', $html);
217
        $attributes = Array
218
        (
219
            'class' => 'new filename',
220
            'id'    => "{$this->_namespace}{$this->name}_e_new_filename",
221
        );
222
        $this->_elements['e_new_filename'] = $this->_form->createElement('text', 'e_new_filename', '', $attributes);
223
224
        // Title Column
225
        $html = "            </td>\n
226
                            <td class=\"new title\">";
227
        $this->_elements['s_new_title'] = $this->_form->createElement('static', 's_new_title', '', $html);
228
        $attributes = Array
229
        (
230
            'class' => 'new title',
231
            'id'    => "{$this->_namespace}{$this->name}_e_new_title",
232
        );
233
        $this->_elements['e_new_title'] = $this->_form->createElement('text', 'e_new_title', '', $attributes);
234
235 View Code Duplication
        if (! $frozen)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
236
        {
237
            // Controls Column
238
            $html = "            </td>\n
239
                                <td class=\"new upload\">";
240
            $this->_elements['s_new_upload'] = $this->_form->createElement('static', 's_new_upload', '', $html);
241
            $attributes = Array
242
            (
243
                'class' => 'new file',
244
                'id'    => "{$this->_namespace}{$this->name}_e_new_file",
245
            );
246
            $this->_elements['e_new_file'] = $this->_form->createElement('file', 'e_new_file', '', $attributes);
247
            $attributes = Array
248
            (
249
                'class' => 'new upload',
250
                'id'    => "{$this->_namespace}{$this->name}_e_new_upload",
251
            );
252
253
            $this->_elements['e_new_upload'] = $this->_form->createElement('submit', "{$this->name}_e_new_upload", $this->_l10n->get('upload file'), $attributes);
254
        }
255
256
        $html = "            </td>\n
257
                         </tr>\n";
258
        $this->_elements['s_new_file'] = $this->_form->createElement('static', 's_new_file', '', $html);
259
    }
260
261
    /**
262
     * Adds the new upload row to the bottom of the table.
263
     *
264
     * @param boolean $frozen Set this to true, if you want to skip all elements which cannot be frozen.
265
     */
266
    private function _add_new_upload_row($frozen)
267
    {
268
        // Show only a configured amount of new image rows
269
        if (   $this->max_count
270
            && count($this->_type->images) >= $this->max_count)
271
        {
272
            return;
273
        }
274
        $html = "        <tr>\n";
275
        if ($this->_type->sortable)
276
        {
277
             $html .= "            <td class=\"new sortable\"></td>\n";
278
        }
279
        // Filename column
280
        $html .= "            <td class=\"new text\" colspan=\"2\">";
281
        $html .= $this->_l10n->get('add new file') . ':';
282
        $this->_elements['s_new_filename'] = $this->_form->createElement('static', 's_new_filename', '', $html);
283
284 View Code Duplication
        if (! $frozen)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
285
        {
286
            // Controls Column
287
            $html = "</td><td class=\"new upload\" colspan=\"2\">";
288
            $this->_elements['s_new_upload'] = $this->_form->createElement('static', 's_new_upload', '', $html);
289
            $attributes = Array
290
            (
291
                'class' => 'new file',
292
                'id'    => "{$this->_namespace}{$this->name}_e_new_file",
293
            );
294
            $this->_elements['e_new_file'] = $this->_form->createElement('file', 'e_new_file', '', $attributes);
295
            $attributes = Array
296
            (
297
                'class' => 'new upload',
298
                'id'    => "{$this->_namespace}{$this->name}_e_new_upload",
299
            );
300
            $this->_elements['e_new_upload'] = $this->_form->createElement('submit', "{$this->name}_e_new_upload", $this->_l10n->get('upload file'), $attributes);
301
        }
302
303
        $html = "            </td>\n
304
                         </tr>\n";
305
        $this->_elements['s_new_file'] = $this->_form->createElement('static', 's_new_file', '', $html);
306
    }
307
308
    /**
309
     * Adds a row for an existing image
310
     *
311
     * @param string $identifier The identifier of the image to add.
312
     * @param array $images The images to add.
313
     * @param boolean $frozen Set this to true, if you want to skip all elements which cannot be frozen.
314
     */
315
    private function _add_image_row($identifier, array $images, $frozen)
0 ignored issues
show
Coding Style introduced by
_add_image_row uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
316
    {
317 View Code Duplication
        if (isset($images['main']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
318
        {
319
            $info = $images['main'];
320
        }
321
        else if (isset($images['original']))
322
        {
323
            $info = $images['original'];
324
        }
325
        else
326
        {
327
            $info = current($images);
328
        }
329
        if (empty($info['object']->guid))
330
        {
331
            //Panic, broken identifier
332
            debug_add("Identifier '{$identifier}' does not have a valid object behind it",  MIDCOM_LOG_ERROR);
333
            return;
334
        }
335
336
        $preview = $this->_get_preview_html($info, $identifier);
337
338
        $img_title = '';
339
        // Some reason we're kicking out-of-sync, check explicitly for POSTed value
340
        if (!empty($_POST[$this->name]["e_exist_{$identifier}_title"]))
341
        {
342
            $img_title = $_POST[$this->name]["e_exist_{$identifier}_title"];
343
        }
344
        // Otherwise use the type title if available
345
        else if (isset($this->_type->titles[$identifier]))
346
        {
347
            $img_title = $this->_type->titles[$identifier];
348
        }
349
350
        // Initialize the string
351
        $sortable = '';
352
353
        if ($this->_type->sortable)
354
        {
355
            $sortable = "            <td class=\"midcom_helper_datamanager2_helper_sortable\"><input type=\"text\" class=\"image_sortable\" name=\"midcom_helper_datamanager2_sortable[{$this->name}][{$identifier}]\" value=\"{$this->_sort_index}\" /></td>\n";
356
            $this->_sort_index++;
357
        }
358
359
        // Filename column
360
        $html = "        <tr title=\"{$info['guid']}\" class=\"midcom_helper_datamanager2_widget_images_image\">\n" .
361
                $sortable .
362
                "            <td class=\"exist filename\" title=\"{$info['filename']}\">\n
363
                                 {$preview}<br />\n
364
                                 <a href=\"{$info['url']}\">{$info['filename']}</a>\n
365
                             </td>\n";
366
        $this->_elements["s_exist_{$identifier}_filename"] = $this->_form->createElement('static', "s_exist_{$identifier}_filename", '', $html);
367
368
        // Title Column, set the value explicitly, as we are sometimes called after the defaults kick in.
369
        $html = "            <td class=\"exist title\" title=\"{$img_title}\">";
370
        $this->_elements["s_exist_{$identifier}_title"] = $this->_form->createElement('static', "s_exist_{$identifier}_title", '', $html);
371
        $attributes = Array
372
        (
373
            'class' => 'exist title',
374
            'id'    => "{$this->_namespace}{$this->name}_e_exist_{$identifier}_title",
375
        );
376
        $this->_elements["e_exist_{$identifier}_title"] = $this->_form->createElement('text', "e_exist_{$identifier}_title", '', $attributes);
377
        $this->_elements["e_exist_{$identifier}_title"]->setValue($img_title);
378
379
        if (!$frozen)
380
        {
381
            $this->_add_controls($info, $identifier);
382
        }
383
384
        $html = "            </td>\n
385
                         </tr>\n";
386
        $this->_elements["s_exist_{$identifier}_file"] = $this->_form->createElement('static', "s_exist_{$identifier}_file", '', $html);
387
    }
388
389
    private function _get_preview_html($info, $identifier)
390
    {
391
        // Get preview image source
392
        if (array_key_exists('thumbnail', $this->_type->images[$identifier]))
393
        {
394
            $url = $this->_type->images[$identifier]['thumbnail']['url'];
395
            $size_line = $this->_type->images[$identifier]['thumbnail']['size_line'];
396
            $preview = "<a href=\"{$info['url']}\" class=\"download\"><img src=\"{$url}\" {$size_line} /></a>";
397
        }
398
        else
399
        {
400
            $url = $info['url'];
401
            $x = $info['size_x'];
402
            $y = $info['size_y'];
403
404
            // Downscale Preview image to max 75px, protect against broken images:
405
            if (   $x != 0
406
                && $y != 0)
407
            {
408
                $aspect = $x/$y;
409
                if ($x > 75)
410
                {
411
                    $x = 75;
412
                    $y = round($x / $aspect);
413
                }
414
                if ($y > 75)
415
                {
416
                    $y = 75;
417
                    $x = round($y * $aspect);
418
                }
419
            }
420
            else
421
            {
422
                // Final safety to prevent the editor from exploding with large images
423
                $x = 75;
424
                $y = 75;
425
            }
426
427
            $size_line = "width=\"{$x}\" height=\"{$y}\"";
428
            $preview = "                <a href=\"{$url}\" class=\"download\"><img src=\"{$url}\" {$size_line} /></a>\n";
429
        }
430
        return $preview;
431
    }
432
433
    private function _add_controls($info, $identifier)
434
    {
435
        if ($info['object']->can_do('midgard:update'))
436
        {
437
            $html = "            </td>\n
438
                                 <td class=\"exist upload\">\n";
439
            $this->_elements["s_exist_{$identifier}_upload"] = $this->_form->createElement('static', "s_exist_{$identifier}_upload", '', $html);
440
            $attributes = Array
441
            (
442
                'class' => 'exist file',
443
                'id'    => "{$this->_namespace}{$this->name}_e_exist_{$identifier}_file",
444
                'onchange' => "midcom_helper_dm2_widget_images_check(event, 'e_exist_{$identifier}_delete')",
445
            );
446
            $this->_elements["e_exist_{$identifier}_file"] = $this->_form->createElement('file', "e_exist_{$identifier}_file", '', $attributes);
447
            $this->_elements["s_exist_{$identifier}_br"] = $this->_form->createElement('static', "s_exist_{$identifier}_upload", '', "<br/>");
448
            $attributes = Array
449
            (
450
                'class' => 'exist upload',
451
                'id'    => "{$this->_namespace}{$this->name}_e_exist_{$identifier}_upload",
452
            );
453
            $this->_elements["e_exist_{$identifier}_upload"] = $this->_form->createElement('submit', "{$this->name}_e_exist_{$identifier}_upload", $this->_l10n->get('replace file'), $attributes);
454
        }
455 View Code Duplication
        if ($info['object']->can_do('midgard:delete'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
456
        {
457
            $attributes = Array
458
            (
459
                'class' => 'exist delete',
460
                'id'    => "{$this->_namespace}{$this->name}_e_exist_{$identifier}_delete",
461
            );
462
            $this->_elements["e_exist_{$identifier}_delete"] = $this->_form->createElement('submit', "{$this->name}_e_exist_{$identifier}_delete", $this->_l10n->get('delete file'), $attributes);
463
        }
464
        // WTF, the identifiers look wonky here
465
        $html = sprintf("<span id=\"e_exist_{$identifier}_delete\" style=\"display:none;color:red\">%s</span>",
466
                        $this->_l10n_midcom->get('You can only upload images here. This file will not be saved.'));
467
        $this->_elements["s_exist_{$identifier}_error"] = $this->_form->createElement('static', "s_exist_{$identifier}_upload", '', $html);
468
    }
469
470
    /**
471
     * Adds the table footer.
472
     *
473
     * @param boolean $frozen Set this to true, if you want to skip all elements which cannot be frozen.
0 ignored issues
show
Bug introduced by
There is no parameter named $frozen. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
474
     */
475
    private function _add_table_footer()
476
    {
477
        $html = "    </tbody>\n
478
                 </table>\n";
479
        $this->_elements['s_footer'] = $this->_form->createElement('static', 's_footer', '', $html);
480
    }
481
482
    /**
483
     * Constructs the upload list.
484
     */
485
    public function add_elements_to_form($attributes)
486
    {
487
        $frozen = false;
488 View Code Duplication
        if (   $this->_type->storage->object
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
489
            && (   !$this->_type->storage->object->can_do('midgard:attachments')
490
                || !$this->_type->storage->object->can_do('midgard:update')
491
                || !$this->_type->storage->object->can_do('midgard:parameters')))
492
        {
493
            $frozen = true;
494
        }
495
        if (!$this->_type->imagemagick_available(true))
496
        {
497
            $frozen = true;
498
        }
499
500
        $this->_compute_elements($frozen);
501
        $this->_group = $this->_form->addGroup($this->_elements, $this->name, $this->_translate($this->_field['title']), "\n");
502
    }
503
504
    /**
505
     * Computes the element list to form the widget. It populates the _elements member, which is
506
     * initialized with a new, empty array during startup.
507
     *
508
     * @param boolean $frozen Set this to true, if you want to skip all elements which cannot be frozen.
509
     */
510 View Code Duplication
    private function _compute_elements($frozen = false)
0 ignored issues
show
Duplication introduced by
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.

Loading history...
511
    {
512
        $this->_elements = Array();
513
514
        $this->_add_table_header($frozen);
515
516
        foreach ($this->_type->images as $identifier => $images)
517
        {
518
            $this->_add_image_row($identifier, $images, $frozen);
519
        }
520
521
        if ($this->set_name_and_title_on_upload)
522
        {
523
            $this->_add_new_upload_row_old($frozen);
524
        }
525
        else
526
        {
527
            $this->_add_new_upload_row($frozen);
528
        }
529
        $this->_add_table_footer();
530
    }
531
532
    /**
533
     * Checks whether a new file has been uploaded. If yes, it is processed.
534
     *
535
     * @param array $values The values associated with our element group (not the full submit value list).
536
     */
537
    private function _check_new_upload($values)
538
    {
539
        if (! array_key_exists('e_new_file', $this->_elements))
540
        {
541
            // We are frozen, no upload can happen, so we exit immediately.
542
            return;
543
        }
544
545
        if (!$this->_elements['e_new_file']->isUploadedFile())
546
        {
547
            // not uploaded file, abort
548
            return;
549
        }
550
551
        $file = $this->_elements['e_new_file']->getValue();
552
553
        if ( preg_match('/\.(zip|tar(\.gz|\.bz2)?|tgz)$/', strtolower($file['name']), $extension_matches))
554
        {
555
            if (! $this->_type->_batch_handler($extension_matches[1], $file))
556
            {
557
                debug_add("Failed to add attachments from compressed files to the field '{$this->name}'. Ignoring silently.", MIDCOM_LOG_WARN);
558
            }
559
            return;
560
        }
561
562
        if (!empty($values['e_new_title']))
563
        {
564
            $title = $values['e_new_title'];
565
        }
566
        else
567
        {
568
            $title = $file['name'];
569
        }
570
571
        if (!empty($values['e_new_filename']))
572
        {
573
            $filename = $values['e_new_filename'];
574
        }
575
        else
576
        {
577
            $filename = $file['name'];
578
        }
579
580 View Code Duplication
        if (! $this->_type->add_image($filename, $file['tmp_name'], $title))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
581
        {
582
            debug_add("Failed to add an attachment to the field '{$this->name}'. Ignoring silently.", MIDCOM_LOG_WARN);
583
        }
584
    }
585
586
    /**
587
     * The following checks are made, in order:
588
     *
589
     * 1. If the delete button was clicked, the image is dropped.
590
     * 2. If a new file has been uploaded, it replaces the current one.
591
     *
592
     * Calls for images which are not listed in the form, will be silently ignored.
593
     * This may happen, for example, if two users edit the same object simultaneoulsy,
594
     * or during addition of new elements.
595
     *
596
     * @param string $identifier The attachment identifier to check for updates.
597
     * @param array $values The values associated with our element group (not the full submit value list).
598
     */
599
    private function _check_for_update($identifier, $values)
600
    {
601
        if (! array_key_exists($identifier, $this->_type->images))
602
        {
603
            // The image does no longer exist
604
            return;
605
        }
606
607
        switch(true)
608
        {
609
            // Image to be deleted
610
            case (array_key_exists("{$this->name}_e_exist_{$identifier}_delete", $values)):
611
                if (! $this->_type->delete_image($identifier))
612
                {
613
                    debug_add("Failed to delete the image {$identifier} on the field '{$this->name}'. Ignoring silently.", MIDCOM_LOG_WARN);
614
                }
615
                break;
616
            // Image to be updated
617
            case (   array_key_exists("e_exist_{$identifier}_file", $this->_elements)
618
                  && $this->_elements["e_exist_{$identifier}_file"]->isUploadedFile()):
619
                $file = $this->_elements["e_exist_{$identifier}_file"]->getValue();
620
                $title = $values["e_exist_{$identifier}_title"];
621
                $filename = $this->_type->images[$identifier]['main']['filename'];
622
623
                if (! $title)
624
                {
625
                    $title = $filename;
626
                }
627
628 View Code Duplication
                if (! $this->_type->update_image($identifier, $filename, $file['tmp_name'], $title))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
629
                {
630
                    debug_add("Failed to update the image {$identifier} on the field '{$this->name}'. Ignoring silently.", MIDCOM_LOG_WARN);
631
                }
632
                break;
633
        }
634
    }
635
636
    /**
637
     * The on_submit event handles all operations immediately. This includes title updates (all
638
     * are done regardless of actual updates).
639
     */
640 View Code Duplication
    function on_submit($results)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
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.

Loading history...
641
    {
642
        if (! array_key_exists($this->name, $results))
643
        {
644
            return;
645
        }
646
647
        $values = $results[$this->name];
648
649
        $this->_check_new_upload($values);
650
651
        foreach (array_keys($this->_type->images) as $identifier)
652
        {
653
            $this->_check_for_update($identifier, $values);
654
        }
655
656
        // Rebuild Widget
657
        $this->_compute_elements();
658
        $this->_group->setElements($this->_elements);
659
    }
660
661
    /**
662
     * Freeze the entire group, special handling applies to skipp all elements which cannot be
663
     * frozen.
664
     */
665
    public function freeze()
666
    {
667
        // Rebuild Widget
668
        $this->_compute_elements(true);
669
        $this->_group->setElements($this->_elements);
670
    }
671
672
    /**
673
     * Unfreeze the entire group, special handling applies, the formgroup is replaced by a the
674
     * full input widget set.
675
     */
676
    public function unfreeze()
677
    {
678
        // Rebuild Widget
679
        $this->_compute_elements(false);
680
        $this->_group->setElements($this->_elements);
681
    }
682
683
    /**
684
     * Prepare for sorting the results
685
     */
686
    public function sync_type_with_widget($results)
0 ignored issues
show
Coding Style introduced by
sync_type_with_widget uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
687
    {
688
        $values = $results[$this->name];
689
        if (   $this->_type->sortable
690
            && isset($_REQUEST['midcom_helper_datamanager2_sortable'])
691
            && isset($_REQUEST['midcom_helper_datamanager2_sortable'][$this->name]))
692
        {
693
            /**
694
             * The regex match was *not* a good idea
695
            $this->_type->_sorted_list = $_REQUEST['midcom_helper_datamanager2_sortable'][$this->name];
696
             */
697
            // Explicitly set scores for each attachment found in the blobs type
698
            $this->_type->_sorted_list = array();
699
            $images_scores = $_REQUEST['midcom_helper_datamanager2_sortable'][$this->name];
700
            $images_scores = array_intersect_key($images_scores, $this->_type->images);
701
            foreach ($images_scores as $images_identifier => $score)
702
            {
703
                foreach ($this->_type->images[$images_identifier] as $info)
704
                {
705
                    /**
706
                     * This is still not 100% perfect (the absolute score values will be "too high" for
707
                     * each attachment stored, but relative to each other they all have correct values)
708
                     * but it works well enough.
709
                     */
710
                    $this->_type->_sorted_list[$info['identifier']] = $score;
711
                }
712
            }
713
        }
714
715
        foreach ($this->_type->images as $identifier => $info)
716
        {
717
            if (!isset($values["e_exist_{$identifier}_title"]))
718
            {
719
                continue;
720
            }
721
            $this->_type->titles[$identifier] = $values["e_exist_{$identifier}_title"];
722
        }
723
    }
724
725
    /**
726
     * Populates the title fields with their defaults.
727
     */
728
    public function get_default()
729
    {
730
        if (sizeof($this->_type->images) == 0)
731
        {
732
            return null;
733
        }
734
        $defaults = array();
735
        foreach (array_keys($this->_type->images) as $identifier)
736
        {
737
            if (isset($this->_type->titles[$identifier]))
738
            {
739
                $defaults["e_exist_{$identifier}_title"] = $this->_type->titles[$identifier];
740
            }
741
            else
742
            {
743
                $defaults["e_exist_{$identifier}_title"] = '';
744
            }
745
        }
746
        return array($this->name => $defaults);
747
    }
748
}
749