Passed
Push — master ( d8ebc1...1c0ff9 )
by Andreas
23:58
created

midcom_services_rcs_handler::_handler_restore()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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

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

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

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

122
        $this->add_diff_button($prefix, $revision2, /** @scrutinizer ignore-type */ $next, 'step-forward', $enabled);
Loading history...
123 4
        $this->add_preview_button($prefix, $next, 'forward', $enabled || $diff_view);
124 4
        $this->add_preview_button($prefix, $last, 'fast-forward', $enabled || $diff_view);
125 4
    }
126
127 4
    private function add_preview_button(string $prefix, array $entry, string $icon, bool $enabled)
128
    {
129 4
        $this->_request_data['rcs_toolbar']->add_item([
130 4
            MIDCOM_TOOLBAR_URL => "{$prefix}preview/{$this->object->guid}/{$entry['revision']}/",
131 4
            MIDCOM_TOOLBAR_LABEL => $entry['version'],
132 4
            MIDCOM_TOOLBAR_GLYPHICON => $icon,
133 4
            MIDCOM_TOOLBAR_ENABLED => $enabled,
134
        ]);
135 4
    }
136
137 4
    private function add_diff_button(string $prefix, array $first, array $second, string $icon, bool $enabled)
138
    {
139 4
        $this->_request_data['rcs_toolbar']->add_item([
140 4
            MIDCOM_TOOLBAR_URL => "{$prefix}diff/{$this->object->guid}/{$first['revision']}/{$second['revision']}/",
141 4
            MIDCOM_TOOLBAR_LABEL => $this->_l10n->get('show differences'),
142 4
            MIDCOM_TOOLBAR_GLYPHICON => $icon,
143 4
            MIDCOM_TOOLBAR_ENABLED => $enabled,
144
        ]);
145 4
    }
146
147 6
    private function prepare_request_data(string $view_title)
148
    {
149 6
        foreach ($this->get_breadcrumbs() as $item) {
150 6
            $this->add_breadcrumb($item[MIDCOM_NAV_URL], $item[MIDCOM_NAV_NAME]);
151
        }
152 6
        $this->add_breadcrumb($this->url_prefix . "{$this->object->guid}/", $this->_l10n->get('show history'));
153
154 6
        $this->_request_data['handler'] = $this;
155 6
        $this->_request_data['view_title'] = $view_title;
156 6
        midcom::get()->head->set_pagetitle($view_title);
157 6
    }
158
159 2
    public function translate(string $string) : string
160
    {
161 2
        $translated = $string;
162 2
        $component = midcom::get()->dbclassloader->get_component_for_class($this->object->__midcom_class_name__);
163 2
        if (midcom::get()->componentloader->is_installed($component)) {
0 ignored issues
show
Bug introduced by
It seems like $component can also be of type null; however, parameter $path of midcom_helper__componentloader::is_installed() does only seem to accept string, 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

163
        if (midcom::get()->componentloader->is_installed(/** @scrutinizer ignore-type */ $component)) {
Loading history...
164 2
            $translated = midcom::get()->i18n->get_l10n($component)->get($string);
0 ignored issues
show
Bug introduced by
It seems like $component can also be of type null; however, parameter $component of midcom_services_i18n::get_l10n() does only seem to accept string, 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

164
            $translated = midcom::get()->i18n->get_l10n(/** @scrutinizer ignore-type */ $component)->get($string);
Loading history...
165
        }
166 2
        if ($translated === $string) {
167 2
            $translated = $this->_l10n->get($string);
168 2
            if ($translated === $string) {
169 2
                $translated = $this->_l10n_midcom->get($string);
170
            }
171
        }
172 2
        return $translated;
173
    }
174
175
    /**
176
     * Show the changes done to the object
177
     */
178 2
    public function _handler_history(Request $request, string $handler_id, array $args)
179
    {
180
        // Check if the comparison request is valid
181 2
        $first = $request->query->get('first');
182 2
        $last = $request->query->get('last');
183 2
        if ($first && $last && $first != $last) {
184
            return new midcom_response_relocate($this->url_prefix . "diff/{$args[0]}/{$first}/{$last}/");
185
        }
186
187 2
        $this->load_object($args[0]);
188 2
        $view_title = sprintf($this->_l10n->get('revision history of %s'), $this->resolve_object_title());
189
190 2
        $this->prepare_request_data($view_title);
191 2
        return $this->handler_callback($handler_id);
192
    }
193
194
    /**
195
     * @param array $data The local request data.
196
     */
197 2
    public function _show_history(string $handler_id, array &$data)
198
    {
199 2
        $data['history'] = $this->backend->get_history();
200 2
        $data['guid'] = $this->object->guid;
201 2
        midcom_show_style($this->style_prefix . 'history');
202 2
    }
203
204
    /**
205
     * Show a diff between two versions
206
     */
207 2
    public function _handler_diff(string $handler_id, array $args, array &$data)
208
    {
209 2
        $this->load_object($args[0]);
210 2
        $history = $this->backend->get_history();
211
212 2
        $compare_revision = $history->get($args[1]);
213 2
        $latest_revision = $history->get($args[2]);
214
215 2
        if (!$compare_revision || !$latest_revision) {
216
            throw new midcom_error_notfound("One of the revisions {$args[1]} or {$args[2]} does not exist.");
217
        }
218
219
        $data['diff'] = array_filter($this->backend->get_diff($args[1], $args[2]), function($value, $key) {
220 2
            return array_key_exists('diff', $value)
221 2
                && !is_array($value['diff'])
222 2
                && midcom_services_rcs::is_field_showable($key);
223 2
        }, ARRAY_FILTER_USE_BOTH);
224 2
        $data['comment'] = $latest_revision;
225 2
        $data['revision_info1'] = $this->render_revision_info($compare_revision);
226 2
        $data['revision_info2'] = $this->render_revision_info($latest_revision);
227
228
        // Set the version numbers
229 2
        $data['guid'] = $args[0];
230
231 2
        $view_title = sprintf($this->_l10n->get('changes between revisions %s and %s'), $compare_revision['version'], $latest_revision['version']);
232
233
        // Load the toolbars
234 2
        $this->rcs_toolbar($compare_revision, $latest_revision);
235 2
        $this->prepare_request_data($view_title);
236 2
        $this->add_breadcrumb(
237 2
            $this->url_prefix . "diff/{$this->object->guid}/{$compare_revision['revision']}/{$latest_revision['revision']}/",
238 2
            sprintf($this->_l10n->get('differences between %s and %s'), $compare_revision['version'], $latest_revision['version'])
239
        );
240
241 2
        return $this->handler_callback($handler_id);
242
    }
243
244 2
    private function render_revision_info(array $metadata) : string
245
    {
246 2
        $output = sprintf($this->_l10n->get('version %s'), $metadata['version']) . ' <span>';
247
248 2
        if ($user = midcom::get()->auth->get_user($metadata['user'])) {
249 2
            $output .= $user->get_storage()->name;
250
        } else {
251
            $user = $this->_l10n_midcom->get('unknown user');
0 ignored issues
show
Unused Code introduced by
The assignment to $user is dead and can be removed.
Loading history...
252
        }
253
254 2
        $output .= ', ' . $this->_l10n->get_formatter()->datetime($metadata['date']) . '</span>';
255
256 2
        return $output;
257
    }
258
259
    /**
260
     * Show the differences between the versions
261
     */
262 2
    public function _show_diff()
263
    {
264 2
        midcom_show_style($this->style_prefix . 'diff');
265 2
    }
266
267
    /**
268
     * View previews
269
     */
270 2
    public function _handler_preview(string $handler_id, array $args, array &$data)
271
    {
272 2
        $revision = $args[1];
273 2
        $data['latest_revision'] = $revision;
274 2
        $data['guid'] = $args[0];
275 2
        $this->load_object($args[0]);
276 2
        $metadata = $this->backend->get_history()->get($args[1]);
277 2
        if (!$metadata) {
278
            throw new midcom_error_notfound("Revision {$args[1]} does not exist.");
279
        }
280
281
        $data['preview'] = array_filter($this->backend->get_revision($revision), function ($value, $key) {
282 2
            return !is_array($value)
283 2
                && !in_array($value, ['', '0000-00-00'])
284 2
                && midcom_services_rcs::is_field_showable($key);
285 2
        }, ARRAY_FILTER_USE_BOTH);
286
287 2
        $schemadb = (new schemabuilder($this->object))->create(null);
288 2
        $data['datamanager'] = (new datamanager($schemadb))
289 2
            ->set_defaults($data['preview'])
290 2
            ->set_storage(new $this->object->__midcom_class_name__);
291
292 2
        $this->_view_toolbar->hide_item($this->url_prefix . "preview/{$this->object->guid}/{$revision}/");
293
294 2
        $view_title = sprintf($this->_l10n->get('viewing version %s from %s'), $metadata['version'], $this->_l10n->get_formatter()->datetime($metadata['date']));
295
        // Load the toolbars
296 2
        $this->rcs_toolbar($metadata);
297 2
        $this->prepare_request_data($view_title);
298 2
        return $this->handler_callback($handler_id);
299
    }
300
301 2
    public function _show_preview()
302
    {
303 2
        midcom_show_style($this->style_prefix . 'preview');
304 2
    }
305
306
    /**
307
     * Restore to diff
308
     */
309
    public function _handler_restore(array $args)
310
    {
311
        $this->load_object($args[0]);
312
313
        $this->object->require_do('midgard:update');
314
        // TODO: set another privilege for restoring?
315
316
        if (   $this->backend->get_history()->version_exists($args[1])
317
            && $this->backend->restore_to_revision($args[1])) {
318
            midcom::get()->uimessages->add($this->_l10n->get('midcom.admin.rcs'), sprintf($this->_l10n->get('restore to version %s successful'), $args[1]));
319
            return new midcom_response_relocate($this->get_object_url());
320
        }
321
        throw new midcom_error(sprintf($this->_l10n->get('restore to version %s failed, reason %s'), $args[1], midcom_connection::get_error_string()));
322
    }
323
}
324