Passed
Push — master ( 19e3b7...6548a2 )
by Andreas
23:32
created

midcom_services_rcs_backend   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 159
Duplicated Lines 0 %

Test Coverage

Coverage 86.84%

Importance

Changes 0
Metric Value
eloc 69
dl 0
loc 159
ccs 66
cts 76
cp 0.8684
rs 10
c 0
b 0
f 0
wmc 16

9 Methods

Rating   Name   Duplication   Size   Complexity  
A restore_to_revision() 0 8 1
A get_history() 0 8 2
A test_config() 0 4 2
A generate_filename() 0 10 2
A run_command() 0 9 2
A __construct() 0 5 1
A read_handle() 0 6 1
A write_object() 0 6 1
A get_diff() 0 46 4
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 61
            debug_add("Directory {$dirpath} does not exist, attempting to create", MIDCOM_LOG_INFO);
53 61
            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 76
    protected function write_object() : string
79
    {
80 76
        $filename = $this->generate_filename();
81 76
        $mapper = new midcom_helper_exporter_xml;
82 76
        file_put_contents($filename, $mapper->object2data($this->object));
83 76
        return $filename;
84
    }
85
86
    /**
87
     * Save a revision of an object, or create a revision if none exists
88
     */
89
    abstract public function update(string $user_id, string $updatemessage = '');
90
91
    /**
92
     * Get a revision
93
     */
94
    abstract public function get_revision(string $revision) : array;
95
96
    /**
97
     * Lists the number of changes that has been done to the object
98
     * Order: The first entry is the newest.
99
     */
100
    abstract protected function load_history() : array;
101
102 8
    public function get_history() : midcom_services_rcs_history
103
    {
104 8
        if ($this->history === null) {
105 8
            $revisions = $this->load_history();
106 8
            $this->history = new midcom_services_rcs_history($revisions);
107
        }
108
109 8
        return $this->history;
110
    }
111
112
    /**
113
     * Get a html diff between two versions.
114
     */
115 2
    public function get_diff(string $oldest_revision, string $latest_revision) : array
116
    {
117 2
        $oldest = $this->get_revision($oldest_revision);
118 2
        $newest = $this->get_revision($latest_revision);
119 2
        $oldest_version = $this->history->get($oldest_revision)['version'] ?? $oldest_revision;
120 2
        $latest_version = $this->history->get($latest_revision)['version'] ?? $latest_revision;
121
122 2
        $return = [];
123
124 2
        $dm = new datamanager((new schemabuilder($this->object))->create(null));
125 2
        $oldest_dm = $dm
126 2
            ->set_defaults($oldest)
127 2
            ->set_storage(new $this->object->__midcom_class_name__)
128 2
            ->render('plaintext');
129 2
        $newest_dm = $dm
130 2
            ->set_defaults($newest)
131 2
            ->set_storage(new $this->object->__midcom_class_name__)
132 2
            ->render('plaintext');
133
134 2
        foreach ($oldest_dm as $attribute => $oldest_value) {
135 2
            if (is_array($oldest_value)) {
136
                continue;
137
            }
138
139 2
            $entry = [
140 2
                'old' => $oldest_value,
141 2
                'new' => $newest_dm[$attribute] ?? ''
142 2
            ];
143
144 2
            if ($entry['old'] != $entry['new']) {
145 2
                $lines1 = explode("\n", $entry['old']);
146 2
                $lines2 = explode("\n", $entry['new']);
147
148 2
                $renderer = new Diff_Renderer_Html_SideBySide([
149 2
                    'old' => $oldest_version,
150 2
                    'new' => $latest_version
151 2
                ]);
152
153 2
                $diff = new Diff($lines1, $lines2);
154
                // Run the diff
155 2
                $entry['diff'] = $diff->render($renderer);
156
            }
157 2
            $return[$attribute] = $entry;
158
        }
159
160 2
        return $return;
161
    }
162
163
    /**
164
     * Restore an object to a certain revision.
165
     */
166
    public function restore_to_revision(string $revision) : bool
167
    {
168
        $new = $this->get_revision($revision);
169
        $mapper = new midcom_helper_exporter_xml();
170
        $this->object = $mapper->data2object($new, $this->object);
171
        $this->object->set_rcs_message("Reverted to revision {$revision}");
172
173
        return $this->object->update();
174
    }
175
}
176