Passed
Push — master ( cef286...5c5891 )
by Andreas
11:44
created

midcom_services_rcs_backend::restore_to_revision()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 8
ccs 0
cts 6
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * @author tarjei huse
4
 * @package midcom.services.rcs
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
use midcom\datamanager\datamanager;
10
use midcom\datamanager\schemabuilder;
11
12
/**
13
 * @package midcom.services.rcs
14
 */
15
abstract class midcom_services_rcs_backend
16
{
17
    /**
18
     * @var midcom_services_rcs_history
19
     */
20
    private $history;
21
22
    /**
23
     * @var midcom_core_dbaobject
24
     */
25
    protected $object;
26
27
    /**
28
     * @var midcom_services_rcs_config
29
     */
30
    protected $config;
31
32 238
    public function __construct(midcom_core_dbaobject $object, midcom_services_rcs_config $config)
33
    {
34 238
        $this->object = $object;
35 238
        $this->config = $config;
36 238
        $this->test_config();
37
    }
38
39 238
    protected function test_config()
40
    {
41 238
        if (!is_writable($this->config->get_rootdir())) {
42
            throw new midcom_error("The root directory {$this->config->get_rootdir()} is not writable!");
43
        }
44
    }
45
46 237
    protected function generate_filename() : string
47
    {
48 237
        $guid = $this->object->guid;
49
        // Keep files organized to subfolders to keep filesystem sane
50 237
        $dirpath = $this->config->get_rootdir() . "/{$guid[0]}/{$guid[1]}";
51 237
        if (!file_exists($dirpath)) {
52 126
            debug_add("Directory {$dirpath} does not exist, attempting to create", MIDCOM_LOG_INFO);
53 126
            mkdir($dirpath, 0777, true);
54
        }
55 237
        return "{$dirpath}/{$guid}";
56
    }
57
58 8
    protected function read_handle(string $command) : array
59
    {
60 8
        $fh = popen($command, "r");
61 8
        $output = stream_get_contents($fh);
62 8
        pclose($fh);
63 8
        return explode("\n", $output);
64
    }
65
66 237
    protected function run_command(string $command)
67
    {
68 237
        $status = $output = null;
69 237
        $command .= ' 2>&1';
70 237
        debug_add("Executing '{$command}'");
71 237
        exec($command, $output, $status);
72 237
        if ($status !== 0) {
73
            debug_print_r('Got output: ', $output);
74
            throw new midcom_error("Command '{$command}' returned with status {$status}:" . implode("\n", $output), MIDCOM_LOG_WARN);
75
        }
76
    }
77
78 233
    protected function write_object(string $filename)
79
    {
80 233
        $mapper = new midcom_helper_exporter_xml;
81 233
        file_put_contents($filename, $mapper->object2data($this->object));
82
    }
83
84
    /**
85
     * Save a revision of an object, or create a revision if none exists
86
     */
87
    abstract public function update(string $user_id, string $updatemessage = '');
88
89
    /**
90
     * Get a revision
91
     */
92
    abstract public function get_revision(string $revision) : array;
93
94
    /**
95
     * Lists the number of changes that has been done to the object
96
     * Order: The first entry is the newest.
97
     */
98
    abstract protected function load_history() : array;
99
100 8
    public function get_history() : midcom_services_rcs_history
101
    {
102 8
        if ($this->history === null) {
103 8
            $revisions = $this->load_history();
104 8
            $this->history = new midcom_services_rcs_history($revisions);
105
        }
106
107 8
        return $this->history;
108
    }
109
110
    /**
111
     * Get a html diff between two versions.
112
     */
113 2
    public function get_diff(string $oldest_revision, string $latest_revision) : array
114
    {
115 2
        $oldest = $this->get_revision($oldest_revision);
116 2
        $newest = $this->get_revision($latest_revision);
117 2
        $oldest_version = $this->history->get($oldest_revision)['version'] ?? $oldest_revision;
118 2
        $latest_version = $this->history->get($latest_revision)['version'] ?? $latest_revision;
119
120 2
        $return = [];
121
122 2
        $dm = new datamanager((new schemabuilder($this->object))->create(null));
123 2
        $oldest_dm = $dm
124 2
            ->set_defaults($oldest)
125 2
            ->set_storage(new $this->object->__midcom_class_name__)
126 2
            ->render('plaintext');
127 2
        $newest_dm = $dm
128 2
            ->set_defaults($newest)
129 2
            ->set_storage(new $this->object->__midcom_class_name__)
130 2
            ->render('plaintext');
131
132 2
        foreach ($oldest_dm as $attribute => $oldest_value) {
133 2
            if (is_array($oldest_value)) {
134
                continue;
135
            }
136
137 2
            $entry = [
138 2
                'old' => $oldest_value,
139 2
                'new' => $newest_dm[$attribute] ?? ''
140 2
            ];
141
142 2
            if ($entry['old'] != $entry['new']) {
143 2
                $lines1 = explode("\n", $entry['old']);
144 2
                $lines2 = explode("\n", $entry['new']);
145
146 2
                $renderer = new Diff_Renderer_Html_SideBySide([
147 2
                    'old' => $oldest_version,
148 2
                    'new' => $latest_version
149 2
                ]);
150
151 2
                $diff = new Diff($lines1, $lines2);
152
                // Run the diff
153 2
                $entry['diff'] = $diff->render($renderer);
154
            }
155 2
            $return[$attribute] = $entry;
156
        }
157
158 2
        return $return;
159
    }
160
161
    /**
162
     * Restore an object to a certain revision.
163
     */
164
    public function restore_to_revision(string $revision) : bool
165
    {
166
        $new = $this->get_revision($revision);
167
        $mapper = new midcom_helper_exporter_xml();
168
        $this->object = $mapper->data2object($new, $this->object);
169
        $this->object->set_rcs_message("Reverted to revision {$revision}");
170
171
        return $this->object->update();
172
    }
173
}
174