Passed
Push — master ( 7131c3...da602a )
by Andreas
30:18
created

midcom_services_rcs_backend::get_diff()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 46
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 4.001

Importance

Changes 0
Metric Value
cc 4
eloc 30
nc 4
nop 2
dl 0
loc 46
ccs 24
cts 25
cp 0.96
crap 4.001
rs 9.44
c 0
b 0
f 0
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 84
    public function __construct(midcom_core_dbaobject $object, midcom_services_rcs_config $config)
33
    {
34 84
        $this->object = $object;
35 84
        $this->config = $config;
36 84
        $this->test_config();
37
    }
38
39 84
    protected function test_config()
40
    {
41 84
        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 82
    protected function generate_filename() : string
47
    {
48 82
        $guid = $this->object->guid;
49
        // Keep files organized to subfolders to keep filesystem sane
50 82
        $dirpath = $this->config->get_rootdir() . "/{$guid[0]}/{$guid[1]}";
51 82
        if (!file_exists($dirpath)) {
52 55
            debug_add("Directory {$dirpath} does not exist, attempting to create", MIDCOM_LOG_INFO);
53 55
            mkdir($dirpath, 0777, true);
54
        }
55 82
        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 80
    protected function run_command(string $command)
67
    {
68 80
        $status = $output = null;
69 80
        $command .= ' 2>&1';
70 80
        debug_add("Executing '{$command}'");
71 80
        exec($command, $output, $status);
72 80
        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
    /**
79
     * Save a revision of an object, or create a revision if none exists
80
     */
81
    abstract public function update(string $updatemessage = '');
82
83
    /**
84
     * Get a revision
85
     */
86
    abstract public function get_revision(string $revision) : array;
87
88
    /**
89
     * Lists the number of changes that has been done to the object
90
     * Order: The first entry is the newest.
91
     */
92
    abstract protected function load_history() : array;
93
94 8
    public function get_history() : midcom_services_rcs_history
95
    {
96 8
        if ($this->history === null) {
97 8
            $revisions = $this->load_history();
98 8
            $this->history = new midcom_services_rcs_history($revisions);
99
        }
100
101 8
        return $this->history;
102
    }
103
104
    /**
105
     * Get a html diff between two versions.
106
     */
107 2
    public function get_diff(string $oldest_revision, string $latest_revision) : array
108
    {
109 2
        $oldest = $this->get_revision($oldest_revision);
110 2
        $newest = $this->get_revision($latest_revision);
111 2
        $oldest_version = $this->history->get($oldest_revision)['version'] ?? $oldest_revision;
112 2
        $latest_version = $this->history->get($latest_revision)['version'] ?? $latest_revision;
113
114 2
        $return = [];
115
116 2
        $dm = new datamanager((new schemabuilder($this->object))->create(null));
117
        $oldest_dm = $dm
118 2
            ->set_defaults($oldest)
119 2
            ->set_storage(new $this->object->__midcom_class_name__)
120 2
            ->render('plaintext');
121
        $newest_dm = $dm
122 2
            ->set_defaults($newest)
123 2
            ->set_storage(new $this->object->__midcom_class_name__)
124 2
            ->render('plaintext');
125
126 2
        foreach ($oldest_dm as $attribute => $oldest_value) {
127 2
            if (is_array($oldest_value)) {
128
                continue;
129
            }
130
131
            $entry = [
132
                'old' => $oldest_value,
133 2
                'new' => $newest_dm[$attribute] ?? ''
134
            ];
135
136 2
            if ($entry['old'] != $entry['new']) {
137 2
                $lines1 = explode("\n", $entry['old']);
138 2
                $lines2 = explode("\n", $entry['new']);
139
140 2
                $renderer = new Diff_Renderer_Html_SideBySide([
141
                    'old' => $oldest_version,
142
                    'new' => $latest_version
143
                ]);
144
145 2
                $diff = new Diff($lines1, $lines2);
146
                // Run the diff
147 2
                $entry['diff'] = $diff->render($renderer);
148
            }
149 2
            $return[$attribute] = $entry;
150
        }
151
152 2
        return $return;
153
    }
154
155
    /**
156
     * Restore an object to a certain revision.
157
     */
158
    public function restore_to_revision(string $revision) : bool
159
    {
160
        $new = $this->get_revision($revision);
161
        $mapper = new midcom_helper_exporter_xml();
162
        $this->object = $mapper->data2object($new, $this->object);
163
        $this->object->set_rcs_message("Reverted to revision {$revision}");
164
165
        return $this->object->update();
166
    }
167
}
168