Passed
Push — master ( 1708b3...8af542 )
by Andreas
11:05 queued 14s
created

midcom_services_rcs_handler::_handler_diff()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 35
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 5.0023

Importance

Changes 0
Metric Value
cc 5
eloc 22
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 35
ccs 21
cts 22
cp 0.9545
crap 5.0023
rs 9.2568
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
    /**
19
     * @var midcom_services_rcs_backend
20
     */
21
    private $backend;
22
23
    /**
24
     * @var midcom_core_dbaobject
25
     */
26
    protected $object;
27
28
    protected $style_prefix = '';
29
30
    protected $url_prefix = '';
31
32
    abstract protected function get_object_url() : string;
33
34
    abstract protected function reply(string $element) : Response;
35
36
    abstract protected function get_breadcrumbs() : array;
37
38 4
    protected function resolve_object_title()
39
    {
40 4
        return midcom_helper_reflector::get($this->object)->get_object_label($this->object);
41
    }
42
43
    /**
44
     * Load the object and the rcs backend
45
     */
46 6
    private function load_object(string $guid)
47
    {
48 6
        $this->object = midcom::get()->dbfactory->get_object_by_guid($guid);
49
50 6
        if (   !midcom::get()->config->get('midcom_services_rcs_enable')
51 6
            || !$this->object->_use_rcs) {
52
            throw new midcom_error_notfound("Revision control not supported for " . get_class($this->object) . ".");
53
        }
54
55 6
        $this->backend = midcom::get()->rcs->load_backend($this->object);
56
    }
57
58
    /**
59
     * Prepare version control toolbar
60
     */
61 4
    private function rcs_toolbar(array $revision, array $revision2 = null)
62
    {
63 4
        $this->add_stylesheet(MIDCOM_STATIC_URL . "/midcom.services.rcs/rcs.css");
64 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

64
        $prefix = /** @scrutinizer ignore-type */ midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX) . $this->url_prefix;
Loading history...
65 4
        $history = $this->backend->get_history();
66 4
        $this->_request_data['rcs_toolbar'] = new midcom_helper_toolbar();
67 4
        $this->populate_rcs_toolbar($history, $prefix, $revision, $revision2);
68
69
        // RCS functional toolbar
70 4
        $this->_request_data['rcs_toolbar_2'] = new midcom_helper_toolbar();
71 4
        $restore = $revision2 ?: $revision;
72
73 4
        $buttons = [
74
            [
75 4
                MIDCOM_TOOLBAR_URL => "{$prefix}{$this->object->guid}/",
76 4
                MIDCOM_TOOLBAR_LABEL => $this->_l10n->get('show history'),
77
                MIDCOM_TOOLBAR_GLYPHICON => 'history',
78
            ], [
79 4
                MIDCOM_TOOLBAR_URL => "{$prefix}restore/{$this->object->guid}/{$restore['revision']}/",
80 4
                MIDCOM_TOOLBAR_LABEL => sprintf($this->_l10n->get('restore version %s'), $restore['version']),
81
                MIDCOM_TOOLBAR_GLYPHICON => 'recycle',
82 4
                MIDCOM_TOOLBAR_ENABLED => ($restore['revision'] !== $history->get_last()['revision']),
83
            ]
84
        ];
85 4
        $this->_request_data['rcs_toolbar_2']->add_items($buttons);
86
    }
87
88 4
    private function populate_rcs_toolbar(midcom_services_rcs_history $history, string $prefix, array $revision, ?array $revision2)
89
    {
90 4
        $first = $history->get_first();
91 4
        $last = $history->get_last();
92
93 4
        $diff_view = !empty($revision2);
94 4
        $revision2 = $revision2 ?? $revision;
95
96 4
        if ($previous = $history->get_previous($revision['revision'])) {
97
            $enabled = true;
98
        } else {
99 4
            $enabled = false;
100 4
            $previous = $first;
101
        }
102 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

102
        $this->add_preview_button($prefix, /** @scrutinizer ignore-type */ $first, 'fast-backward', $enabled || $diff_view);
Loading history...
103 4
        $this->add_preview_button($prefix, $diff_view ? $revision : $previous, 'backward', $enabled || $diff_view);
104 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

104
        $this->add_diff_button($prefix, /** @scrutinizer ignore-type */ $previous, $revision, 'step-backward', $enabled);
Loading history...
105
106 4
        $this->_request_data['rcs_toolbar']->add_item([
107 4
            MIDCOM_TOOLBAR_URL => "{$prefix}preview/{$this->object->guid}/{$revision2['revision']}/",
108 4
            MIDCOM_TOOLBAR_LABEL => sprintf($this->_l10n->get('version %s'), $revision2['version']),
109
            MIDCOM_TOOLBAR_GLYPHICON => 'file-o',
110
            MIDCOM_TOOLBAR_ENABLED => $diff_view,
111
        ]);
112
113 4
        if ($next = $history->get_next($revision2['revision'])) {
114 2
            $enabled = true;
115
        } else {
116 2
            $enabled = false;
117 2
            $next = $last;
118
        }
119 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

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