Passed
Push — master ( 1bf489...65f4f0 )
by Andreas
10:01
created

midcom_services_rcs_handler   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Test Coverage

Coverage 90.51%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 144
dl 0
loc 278
ccs 143
cts 158
cp 0.9051
rs 9.1199
c 1
b 0
f 0
wmc 41

14 Methods

Rating   Name   Duplication   Size   Complexity  
A prepare_request_data() 0 10 2
A add_diff_button() 0 7 1
A add_preview_button() 0 7 1
A resolve_object_title() 0 3 1
B populate_rcs_toolbar() 0 34 8
A translate() 0 13 4
A _handler_diff() 0 35 5
A rcs_toolbar() 0 25 2
A prepare_backend() 0 10 3
A _handler_history() 0 16 4
A load_object() 0 3 1
A render_revision_info() 0 13 2
A _handler_preview() 0 27 4
A _handler_restore() 0 13 3

How to fix   Complexity   

Complex Class

Complex classes like midcom_services_rcs_handler 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.

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_services_rcs_handler, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package midcom.services.rcs
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 */
7
8
use Symfony\Component\HttpFoundation\Request;
9
use midcom\datamanager\schemabuilder;
10
use midcom\datamanager\datamanager;
11
use Symfony\Component\HttpFoundation\Response;
12
13
/**
14
 * @package midcom.services.rcs
15
 */
16
abstract class midcom_services_rcs_handler extends midcom_baseclasses_components_handler
17
{
18
    private midcom_services_rcs_backend $backend;
19
20
    protected midcom_core_dbaobject $object;
21
22
    protected string $style_prefix = '';
23
24
    protected string $url_prefix = '';
25
26
    abstract protected function get_object_url() : string;
27
28
    abstract protected function reply(string $element) : Response;
29
30
    abstract protected function get_breadcrumbs() : array;
31
32 4
    protected function resolve_object_title()
33
    {
34 4
        return midcom_helper_reflector::get($this->object)->get_object_label($this->object);
35
    }
36
37 6
    protected function load_object(string $guid) : midcom_core_dbaobject
38
    {
39 6
        return midcom::get()->dbfactory->get_object_by_guid($guid);
40
    }
41
42
    /**
43
     * Load the object and the rcs backend
44
     */
45 6
    private function prepare_backend(string $guid)
46
    {
47 6
        $this->object = $this->load_object($guid);
48
49 6
        if (   !midcom::get()->config->get('midcom_services_rcs_enable')
50 6
            || !$this->object->_use_rcs) {
51
            throw new midcom_error_notfound("Revision control not supported for " . get_class($this->object) . ".");
52
        }
53
54 6
        $this->backend = midcom::get()->rcs->load_backend($this->object);
55
    }
56
57
    /**
58
     * Prepare version control toolbar
59
     */
60 4
    private function rcs_toolbar(array $revision, array $revision2 = null)
61
    {
62 4
        $this->add_stylesheet(MIDCOM_STATIC_URL . "/midcom.services.rcs/rcs.css");
63 4
        $prefix = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX) . $this->url_prefix;
0 ignored issues
show
Bug introduced by
Are you sure midcom_core_context::get...M_CONTEXT_ANCHORPREFIX) of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

63
        $prefix = /** @scrutinizer ignore-type */ midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX) . $this->url_prefix;
Loading history...
64 4
        $history = $this->backend->get_history();
65 4
        $this->_request_data['rcs_toolbar'] = new midcom_helper_toolbar();
66 4
        $this->populate_rcs_toolbar($history, $prefix, $revision, $revision2);
67
68
        // RCS functional toolbar
69 4
        $this->_request_data['rcs_toolbar_2'] = new midcom_helper_toolbar();
70 4
        $restore = $revision2 ?: $revision;
71
72 4
        $buttons = [
73 4
            [
74 4
                MIDCOM_TOOLBAR_URL => "{$prefix}{$this->object->guid}/",
75 4
                MIDCOM_TOOLBAR_LABEL => $this->_l10n->get('show history'),
76 4
                MIDCOM_TOOLBAR_GLYPHICON => 'history',
77 4
            ], [
78 4
                MIDCOM_TOOLBAR_URL => "{$prefix}restore/{$this->object->guid}/{$restore['revision']}/",
79 4
                MIDCOM_TOOLBAR_LABEL => sprintf($this->_l10n->get('restore version %s'), $restore['version']),
80 4
                MIDCOM_TOOLBAR_GLYPHICON => 'recycle',
81 4
                MIDCOM_TOOLBAR_ENABLED => ($restore['revision'] !== $history->get_last()['revision']),
82 4
            ]
83 4
        ];
84 4
        $this->_request_data['rcs_toolbar_2']->add_items($buttons);
85
    }
86
87 4
    private function populate_rcs_toolbar(midcom_services_rcs_history $history, string $prefix, array $revision, ?array $revision2)
88
    {
89 4
        $first = $history->get_first();
90 4
        $last = $history->get_last();
91
92 4
        $diff_view = !empty($revision2);
93 4
        $revision2 = $revision2 ?? $revision;
94
95 4
        if ($previous = $history->get_previous($revision['revision'])) {
96
            $enabled = true;
97
        } else {
98 4
            $enabled = false;
99 4
            $previous = $first;
100
        }
101 4
        $this->add_preview_button($prefix, $first, 'fast-backward', $enabled || $diff_view);
0 ignored issues
show
Bug introduced by
It seems like $first can also be of type null; however, parameter $entry of midcom_services_rcs_handler::add_preview_button() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
        $this->add_preview_button($prefix, /** @scrutinizer ignore-type */ $first, 'fast-backward', $enabled || $diff_view);
Loading history...
102 4
        $this->add_preview_button($prefix, $diff_view ? $revision : $previous, 'backward', $enabled || $diff_view);
103 4
        $this->add_diff_button($prefix, $previous, $revision, 'step-backward', $enabled);
0 ignored issues
show
Bug introduced by
It seems like $previous can also be of type null; however, parameter $first of midcom_services_rcs_handler::add_diff_button() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

103
        $this->add_diff_button($prefix, /** @scrutinizer ignore-type */ $previous, $revision, 'step-backward', $enabled);
Loading history...
104
105 4
        $this->_request_data['rcs_toolbar']->add_item([
106 4
            MIDCOM_TOOLBAR_URL => "{$prefix}preview/{$this->object->guid}/{$revision2['revision']}/",
107 4
            MIDCOM_TOOLBAR_LABEL => sprintf($this->_l10n->get('version %s'), $revision2['version']),
108 4
            MIDCOM_TOOLBAR_GLYPHICON => 'file-o',
109 4
            MIDCOM_TOOLBAR_ENABLED => $diff_view,
110 4
        ]);
111
112 4
        if ($next = $history->get_next($revision2['revision'])) {
113 2
            $enabled = true;
114
        } else {
115 2
            $enabled = false;
116 2
            $next = $last;
117
        }
118 4
        $this->add_diff_button($prefix, $revision2, $next, 'step-forward', $enabled);
0 ignored issues
show
Bug introduced by
It seems like $next can also be of type null; however, parameter $second of midcom_services_rcs_handler::add_diff_button() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
        $this->add_diff_button($prefix, $revision2, /** @scrutinizer ignore-type */ $next, 'step-forward', $enabled);
Loading history...
119 4
        $this->add_preview_button($prefix, $next, 'forward', $enabled || $diff_view);
120 4
        $this->add_preview_button($prefix, $last, 'fast-forward', $enabled || $diff_view);
121
    }
122
123 4
    private function add_preview_button(string $prefix, array $entry, string $icon, bool $enabled)
124
    {
125 4
        $this->_request_data['rcs_toolbar']->add_item([
126 4
            MIDCOM_TOOLBAR_URL => "{$prefix}preview/{$this->object->guid}/{$entry['revision']}/",
127 4
            MIDCOM_TOOLBAR_LABEL => $entry['version'],
128 4
            MIDCOM_TOOLBAR_GLYPHICON => $icon,
129 4
            MIDCOM_TOOLBAR_ENABLED => $enabled,
130 4
        ]);
131
    }
132
133 4
    private function add_diff_button(string $prefix, array $first, array $second, string $icon, bool $enabled)
134
    {
135 4
        $this->_request_data['rcs_toolbar']->add_item([
136 4
            MIDCOM_TOOLBAR_URL => "{$prefix}diff/{$this->object->guid}/{$first['revision']}/{$second['revision']}/",
137 4
            MIDCOM_TOOLBAR_LABEL => $this->_l10n->get('show differences'),
138 4
            MIDCOM_TOOLBAR_GLYPHICON => $icon,
139 4
            MIDCOM_TOOLBAR_ENABLED => $enabled,
140 4
        ]);
141
    }
142
143 6
    private function prepare_request_data(string $view_title)
144
    {
145 6
        foreach ($this->get_breadcrumbs() as $item) {
146 6
            $this->add_breadcrumb($item[MIDCOM_NAV_URL], $item[MIDCOM_NAV_NAME]);
147
        }
148 6
        $this->add_breadcrumb($this->url_prefix . "{$this->object->guid}/", $this->_l10n->get('show history'));
149
150 6
        $this->_request_data['handler'] = $this;
151 6
        $this->_request_data['view_title'] = $view_title;
152 6
        midcom::get()->head->set_pagetitle($view_title);
153
    }
154
155 2
    public function translate(string $string) : string
156
    {
157 2
        $stack = [$this->_component, 'midcom'];
158 2
        if ($component = midcom::get()->dbclassloader->get_component_for_class($this->object->__midcom_class_name__)) {
159 2
            array_unshift($stack, $component);
160
        }
161 2
        foreach ($stack as $db) {
162 2
            $translated = $this->_i18n->get_string($string, $db);
163 2
            if ($translated != $string) {
164
                return $translated;
165
            }
166
        }
167 2
        return $string;
168
    }
169
170
    /**
171
     * Show the changes done to the object
172
     */
173 2
    public function _handler_history(Request $request, array &$data, array $args)
174
    {
175
        // Check if the comparison request is valid
176 2
        $first = $request->query->get('first');
177 2
        $last = $request->query->get('last');
178 2
        if ($first && $last && $first != $last) {
179
            return new midcom_response_relocate($this->url_prefix . "diff/{$args[0]}/{$first}/{$last}/");
180
        }
181
182 2
        $this->prepare_backend($args[0]);
183 2
        $view_title = sprintf($this->_l10n->get('revision history of %s'), $this->resolve_object_title());
184
185 2
        $this->prepare_request_data($view_title);
186 2
        $data['history'] = $this->backend->get_history();
187 2
        $data['guid'] = $this->object->guid;
188 2
        return $this->reply($this->style_prefix . 'history');
189
    }
190
191
    /**
192
     * Show a diff between two versions
193
     */
194 2
    public function _handler_diff(array $args, array &$data)
195
    {
196 2
        $this->prepare_backend($args[0]);
197 2
        $history = $this->backend->get_history();
198
199 2
        $compare_revision = $history->get($args[1]);
200 2
        $latest_revision = $history->get($args[2]);
201
202 2
        if (!$compare_revision || !$latest_revision) {
203
            throw new midcom_error_notfound("One of the revisions {$args[1]} or {$args[2]} does not exist.");
204
        }
205
206 2
        $data['diff'] = array_filter($this->backend->get_diff($args[1], $args[2]), function($value, $key) {
207 2
            return array_key_exists('diff', $value)
208 2
                && !is_array($value['diff'])
209 2
                && midcom_services_rcs::is_field_showable($key);
210 2
        }, ARRAY_FILTER_USE_BOTH);
211 2
        $data['comment'] = $latest_revision;
212 2
        $data['revision_info1'] = $this->render_revision_info($compare_revision);
213 2
        $data['revision_info2'] = $this->render_revision_info($latest_revision);
214
215
        // Set the version numbers
216 2
        $data['guid'] = $args[0];
217
218 2
        $view_title = sprintf($this->_l10n->get('changes between revisions %s and %s'), $compare_revision['version'], $latest_revision['version']);
219
220
        // Load the toolbars
221 2
        $this->rcs_toolbar($compare_revision, $latest_revision);
222 2
        $this->prepare_request_data($view_title);
223 2
        $this->add_breadcrumb(
224 2
            $this->url_prefix . "diff/{$this->object->guid}/{$compare_revision['revision']}/{$latest_revision['revision']}/",
225 2
            sprintf($this->_l10n->get('differences between %s and %s'), $compare_revision['version'], $latest_revision['version'])
226 2
        );
227
228 2
        return $this->reply($this->style_prefix . 'diff');
229
    }
230
231 2
    private function render_revision_info(array $metadata) : string
232
    {
233 2
        $output = sprintf($this->_l10n->get('version %s'), $metadata['version']) . ' <span>';
234
235 2
        if ($user = midcom::get()->auth->get_user($metadata['user'])) {
236 2
            $output .= $user->get_storage()->name;
237
        } else {
238
            $output .= $this->_l10n_midcom->get('unknown user');
239
        }
240 2
        $longdate = $this->_l10n->get_formatter()->datetime($metadata['date'], IntlDateFormatter::MEDIUM, IntlDateFormatter::LONG);
241 2
        $output .= ', <span title="' . $longdate . '">' . $this->_l10n->get_formatter()->datetime($metadata['date']) . '</span></span>';
242
243 2
        return $output;
244
    }
245
246
    /**
247
     * View previews
248
     */
249 2
    public function _handler_preview(array $args, array &$data)
250
    {
251 2
        $revision = $args[1];
252 2
        $data['latest_revision'] = $revision;
253 2
        $data['guid'] = $args[0];
254 2
        $this->prepare_backend($args[0]);
255 2
        $metadata = $this->backend->get_history()->get($args[1]);
256 2
        if (!$metadata) {
257
            throw new midcom_error_notfound("Revision {$args[1]} does not exist.");
258
        }
259
260 2
        $data['preview'] = array_filter($this->backend->get_revision($revision), function ($value, $key) {
261 2
            return !is_array($value)
262 2
                && !in_array($value, ['', '0000-00-00'])
263 2
                && midcom_services_rcs::is_field_showable($key);
264 2
        }, ARRAY_FILTER_USE_BOTH);
265
266 2
        $schemadb = (new schemabuilder($this->object))->create(null);
267 2
        $data['datamanager'] = (new datamanager($schemadb))
268 2
            ->set_defaults($data['preview'])
269 2
            ->set_storage(new $this->object->__midcom_class_name__);
270
271 2
        $view_title = sprintf($this->_l10n->get('viewing version %s from %s'), $metadata['version'], $this->_l10n->get_formatter()->datetime($metadata['date']));
272
        // Load the toolbars
273 2
        $this->rcs_toolbar($metadata);
274 2
        $this->prepare_request_data($view_title);
275 2
        return $this->reply($this->style_prefix . 'preview');
276
    }
277
278
    /**
279
     * Restore to diff
280
     */
281
    public function _handler_restore(array $args)
282
    {
283
        $this->prepare_backend($args[0]);
284
285
        $this->object->require_do('midgard:update');
286
        // TODO: set another privilege for restoring?
287
288
        if (   $this->backend->get_history()->version_exists($args[1])
289
            && $this->backend->restore_to_revision($args[1])) {
290
            midcom::get()->uimessages->add($this->_l10n->get('midcom.admin.rcs'), sprintf($this->_l10n->get('restore to version %s successful'), $args[1]));
291
            return new midcom_response_relocate($this->get_object_url());
292
        }
293
        throw new midcom_error(sprintf($this->_l10n->get('restore to version %s failed, reason %s'), $args[1], midcom_connection::get_error_string()));
294
    }
295
}
296