Completed
Push — master ( 7862a4...44d3af )
by Andreas
07:38
created

midgard_admin_asgard_handler_object_attachments   C

Complexity

Total Complexity 40

Size/Duplication

Total Lines 318
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
dl 0
loc 318
rs 6.6773
c 0
b 0
f 0
wmc 40
lcom 1
cbo 17

12 Methods

Rating   Name   Duplication   Size   Complexity  
A _on_initialize() 0 5 1
B _process_file_upload() 0 24 4
C _process_form() 0 65 14
B _get_file() 0 25 4
A _list_files() 0 9 1
A _add_jscripts() 0 15 2
A prepare_object() 0 7 1
A _handler_create() 0 15 2
A _show_create() 0 10 1
C _handler_edit() 0 46 8
A _show_edit() 0 9 1
A _handler_delete() 0 15 1

How to fix   Complexity   

Complex Class

Complex classes like midgard_admin_asgard_handler_object_attachments 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 midgard_admin_asgard_handler_object_attachments, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package midgard.admin.asgard
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
 * Attachment editing interface
11
 *
12
 * @package midgard.admin.asgard
13
 */
14
class midgard_admin_asgard_handler_object_attachments extends midcom_baseclasses_components_handler
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...
15
{
16
    /**
17
     * Current loaded object
18
     *
19
     * @var midcom_core_dbaobject
20
     */
21
    private $_object = null;
22
23
    /**
24
     * Files in the current object
25
     *
26
     * @var array
27
     */
28
    private $_files = array();
29
30
    /**
31
     * Current file being edited
32
     *
33
     * @var midcom_db_attachment
34
     */
35
    private $_file = null;
36
37
    public function _on_initialize()
38
    {
39
        $this->add_stylesheet(MIDCOM_STATIC_URL . '/midcom.helper.datamanager2/legacy.css');
40
        $this->add_stylesheet(MIDCOM_STATIC_URL . '/midgard.admin.asgard/attachments/layout.css');
41
    }
42
43
    private function _process_file_upload($uploaded_file)
44
    {
45
        if (is_null($this->_file))
46
        {
47
            $local_filename = midcom_db_attachment::safe_filename($uploaded_file['name']);
48
            $local_file = $this->_get_file($local_filename, true);
49
        }
50
        else
51
        {
52
            $local_file = $this->_file;
53
        }
54
55
        if ($local_file->mimetype != $uploaded_file['type'])
56
        {
57
            $local_file->mimetype = $uploaded_file['type'];
58
            $local_file->update();
59
        }
60
61
        if (!$local_file->copy_from_file($uploaded_file['tmp_name']))
62
        {
63
            return false;
64
        }
65
        return $local_file->name;
66
    }
67
68
    private function _process_form()
0 ignored issues
show
Coding Style introduced by
_process_form 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...
Coding Style introduced by
_process_form uses the super-global variable $_FILES 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...
69
    {
70
        if (!isset($_POST['midgard_admin_asgard_save']))
71
        {
72
            return false;
73
        }
74
75
        // Check if we have an uploaded file
76
        if (   isset($_FILES['midgard_admin_asgard_file'])
77
            && is_uploaded_file($_FILES['midgard_admin_asgard_file']['tmp_name']))
78
        {
79
            return $this->_process_file_upload($_FILES['midgard_admin_asgard_file']);
80
        }
81
82
        if (is_null($this->_file))
83
        {
84
            if (empty($_POST['midgard_admin_asgard_filename']))
85
            {
86
                return false;
87
            }
88
89
            // We're creating a new file
90
            $local_filename = midcom_db_attachment::safe_filename($_POST['midgard_admin_asgard_filename']);
91
            $local_file = $this->_get_file($local_filename, true);
92
        }
93
        else
94
        {
95
            $local_file = $this->_file;
96
        }
97
98
        $needs_update = false;
99
100
        if (   !empty($_POST['midgard_admin_asgard_filename'])
101
            && $local_file->name != $_POST['midgard_admin_asgard_filename'])
102
        {
103
            $local_file->name = $_POST['midgard_admin_asgard_filename'];
104
            $needs_update = true;
105
        }
106
107
        if (   !empty($_POST['midgard_admin_asgard_mimetype'])
108
            && $local_file->mimetype != $_POST['midgard_admin_asgard_mimetype'])
109
        {
110
            $local_file->mimetype = $_POST['midgard_admin_asgard_mimetype'];
111
            $needs_update = true;
112
        }
113
114
        if (   $needs_update
115
            && !$local_file->update())
116
        {
117
            return false;
118
        }
119
120
        // We should always store at least an empty string so it can be edited later
121
        $contents = '';
122
        if (!empty($_POST['midgard_admin_asgard_contents']))
123
        {
124
            $contents = $_POST['midgard_admin_asgard_contents'];
125
        }
126
127
        if (!$local_file->copy_from_memory($contents))
128
        {
129
            return false;
130
        }
131
        return $local_file->name;
132
    }
133
134
    /**
135
     *
136
     * @param string $filename
137
     * @param boolean $autocreate
138
     * @return midcom_db_attachment
139
     */
140
    private function _get_file($filename, $autocreate = false)
141
    {
142
        $qb = midcom_db_attachment::new_query_builder();
143
        $qb->add_constraint('parentguid', '=', $this->_object->guid);
144
        $qb->add_constraint('name', '=', $filename);
145
146
        $files = $qb->execute();
147
        if (empty($files))
148
        {
149
            if (!$autocreate)
150
            {
151
                throw new midcom_error_notfound("Attachment '{$filename}' of object {$this->_object->guid} was not found.");
152
            }
153
            $file = new midcom_db_attachment();
154
            $file->name = $filename;
155
            $file->parentguid = $this->_object->guid;
156
157
            if (!$file->create())
158
            {
159
                throw new midcom_error('Failed to create attachment, reason: ' . midcom_connection::get_error_string());
160
            }
161
            return $file;
162
        }
163
        return $files[0];
164
    }
165
166
    private function _list_files()
167
    {
168
        $qb = midcom_db_attachment::new_query_builder();
169
        $qb->add_constraint('parentguid', '=', $this->_object->guid);
170
        $qb->add_order('mimetype');
171
        $qb->add_order('metadata.score', 'DESC');
172
        $qb->add_order('name');
173
        $this->_files = $qb->execute();
0 ignored issues
show
Documentation Bug introduced by
It seems like $qb->execute() can also be of type false. However, the property $_files is declared as type array. 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 $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

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;
}
Loading history...
174
    }
175
176
    /**
177
     * Add the necessary files for attachment operations, if attachments exist
178
     */
179
    private function _add_jscripts()
180
    {
181
        if (sizeof($this->_files) > 0)
182
        {
183
            // Add Colorbox
184
            midcom::get()->head->add_jsfile(MIDCOM_STATIC_URL . '/jQuery/colorbox/jquery.colorbox-min.js');
185
            $this->add_stylesheet(MIDCOM_STATIC_URL . '/jQuery/colorbox/colorbox.css', 'screen');
186
            midcom::get()->head->add_jsfile(MIDCOM_STATIC_URL . '/midgard.admin.asgard/object_browser.js');
187
188
            //add table widget
189
            midcom::get()->head->add_jsfile(MIDCOM_STATIC_URL . '/jQuery/jquery.tablesorter.pack.js');
190
            $this->add_stylesheet(MIDCOM_STATIC_URL . '/midgard.admin.asgard/tablewidget.css');
191
            midcom\workflow\delete::add_head_elements();
192
        }
193
    }
194
195
    private function prepare_object($guid)
196
    {
197
        midcom::get()->auth->require_user_do('midgard.admin.asgard:manage_objects', null, 'midgard_admin_asgard_plugin');
198
        $this->_object = midcom::get()->dbfactory->get_object_by_guid($guid);
199
        $this->_object->require_do('midgard:update');
200
        $this->_object->require_do('midgard:attachments');
201
    }
202
203
    /**
204
     * Handler for creating new attachments
205
     *
206
     * @param string $handler_id Name of the used handler
207
     * @param array $args Array containing the variable arguments passed to the handler
208
     * @param array &$data Data passed to the show method
209
     */
210
    public function _handler_create($handler_id, array $args, array &$data)
211
    {
212
        $this->prepare_object($args[0]);
213
214
        if ($filename = $this->_process_form())
215
        {
216
            return new midcom_response_relocate("__mfa/asgard/object/attachments/{$this->_object->guid}/{$filename}/");
217
        }
218
219
        $this->_list_files();
220
        $this->_add_jscripts();
221
222
        midgard_admin_asgard_plugin::bind_to_object($this->_object, $handler_id, $data);
223
        return new midgard_admin_asgard_response($this, '_show_create');
224
    }
225
226
    /**
227
     * @param string $handler_id Name of the used handler
228
     * @param array &$data Data passed to the show method
229
     */
230
    public function _show_create($handler_id, array &$data)
231
    {
232
        $data['files'] = $this->_files;
233
        $data['object'] = $this->_object;
234
        midcom_show_style('midgard_admin_asgard_object_attachments_header');
235
236
        $data['attachment_text_types'] = $this->_config->get('attachment_text_types');
237
        midcom_show_style('midgard_admin_asgard_object_attachments_new');
238
        midcom_show_style('midgard_admin_asgard_object_attachments_footer');
239
    }
240
241
    /**
242
     * @param string $handler_id Name of the used handler
243
     * @param array $args Array containing the variable arguments passed to the handler
244
     * @param array &$data Data passed to the show method
245
     */
246
    public function _handler_edit($handler_id, array $args, array &$data)
247
    {
248
        $this->prepare_object($args[0]);
249
250
        $data['filename'] = $args[1];
251
        $this->_file = $this->_get_file($data['filename']);
252
        $this->_file->require_do('midgard:update');
253
        $this->bind_view_to_object($this->_file);
254
255
        $filename = $this->_process_form();
256
        if (   $filename
257
            && $filename != $data['filename'])
258
        {
259
            return new midcom_response_relocate("__mfa/asgard/object/attachments/{$this->_object->guid}/{$filename}/");
260
        }
261
262
        $this->_list_files();
263
        $this->_add_jscripts();
264
265
        $data['attachment_text_types'] = $this->_config->get('attachment_text_types');
266
        if (array_key_exists($this->_file->mimetype, $data['attachment_text_types']))
267
        {
268
            // Figure out correct syntax from MIME type
269
            switch(preg_replace('/.+?\//', '', $this->_file->mimetype))
270
            {
271
                case 'css':
272
                    $data['file_syntax'] = 'css';
273
                    break;
274
275
                case 'html':
276
                    $data['file_syntax'] = 'html';
277
                    break;
278
279
                case 'x-javascript':
280
                case 'javascript':
281
                    $data['file_syntax'] = 'javascript';
282
                    break;
283
284
                default:
285
                    $data['file_syntax'] = 'text';
286
            }
287
        }
288
289
        midgard_admin_asgard_plugin::bind_to_object($this->_object, $handler_id, $data);
290
        return new midgard_admin_asgard_response($this, '_show_edit');
291
    }
292
293
    /**
294
     * Show the editing view for the requested style
295
     *
296
     * @param string $handler_id Name of the used handler
297
     * @param array &$data Data passed to the show method
298
     */
299
    public function _show_edit($handler_id, array &$data)
300
    {
301
        $data['files'] = $this->_files;
302
        $data['file'] = $this->_file;
303
        $data['object'] = $this->_object;
304
        midcom_show_style('midgard_admin_asgard_object_attachments_header');
305
        midcom_show_style('midgard_admin_asgard_object_attachments_file');
306
        midcom_show_style('midgard_admin_asgard_object_attachments_footer');
307
    }
308
309
    /**
310
     * Handler for confirming file deleting for the requested file
311
     *
312
     * @param string $handler_id Name of the used handler
313
     * @param array $args Array containing the variable arguments passed to the handler
314
     * @param array &$data Data passed to the show method
315
     */
316
    public function _handler_delete($handler_id, array $args, array &$data)
317
    {
318
        $this->prepare_object($args[0]);
319
320
        $filename = $args[1];
321
        $file = $this->_get_file($filename);
322
323
        $workflow = $this->get_workflow('delete', array
324
        (
325
            'object' => $file,
326
            'label' => $filename,
327
            'success_url' => "__mfa/asgard/object/attachments/{$this->_object->guid}/"
328
        ));
329
        return $workflow->run();
330
    }
331
}
332