Passed
Push — master ( 7973ad...4d8582 )
by Andreas
18:26
created

midcom_services_rcs_backend::read_handle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 6
ccs 5
cts 5
cp 1
crap 1
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
     * Cached revision history for the object
19
     *
20
     * @var midcom_services_rcs_history
21
     */
22
    private $history;
23
24
    /**
25
     * The current object
26
     */
27
    protected $object;
28
29
    /**
30
     * @var midcom_services_rcs_config
31
     */
32
    protected $config;
33
34 83
    public function __construct($object, midcom_services_rcs_config $config)
35
    {
36 83
        $this->object = $object;
37 83
        $this->config = $config;
38 83
        $this->test_config();
39 83
    }
40
41 83
    protected function test_config()
42
    {
43 83
        if (!is_writable($this->config->get_rootdir())) {
44
            throw new midcom_error("The root directory {$this->config->get_rootdir()} is not writable!");
45
        }
46 83
    }
47
48 81
    protected function generate_filename() : string
49
    {
50 81
        $guid = $this->object->guid;
51
        // Keep files organized to subfolders to keep filesystem sane
52 81
        $dirpath = $this->config->get_rootdir() . "/{$guid[0]}/{$guid[1]}";
53 81
        if (!file_exists($dirpath)) {
54 58
            debug_add("Directory {$dirpath} does not exist, attempting to create", MIDCOM_LOG_INFO);
55 58
            mkdir($dirpath, 0777, true);
56
        }
57 81
        return "{$dirpath}/{$guid}";
58
    }
59
60 8
    protected function read_handle(string $command) : array
61
    {
62 8
        $fh = popen($command, "r");
63 8
        $output = stream_get_contents($fh);
64 8
        pclose($fh);
65 8
        return explode("\n", $output);
66
    }
67
68 79
    protected function run_command(string $command)
69
    {
70 79
        $status = $output = null;
71 79
        $command .= ' 2>&1';
72 79
        debug_add("Executing '{$command}'");
73 79
        exec($command, $output, $status);
74 79
        if ($status !== 0) {
75
            debug_print_r('Got output: ', $output);
76
            throw new midcom_error("Command '{$command}' returned with status {$status}:" . implode("\n", $output), MIDCOM_LOG_WARN);
77
        }
78 79
    }
79
80
    /**
81
     * Save a revision of an object, or create a revision if none exists
82
     *
83
     * @param midcom_core_dbaobject $object the object to save.
84
     * @param string $updatemessage the message to be saved with the object.
85
     * @throws midcom_error on serious errors.
86
     */
87
    abstract public function update(string $updatemessage = null);
88
89
    /**
90
     * Get the object of a revision
91
     *
92
     * @return array array representation of the object
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
        $oldest_dm = $dm
126 2
            ->set_defaults($oldest)
127 2
            ->set_storage(new $this->object->__midcom_class_name__)
128 2
            ->get_content_csv();
129
        $newest_dm = $dm
130 2
            ->set_defaults($newest)
131 2
            ->set_storage(new $this->object->__midcom_class_name__)
132 2
            ->get_content_csv();
133
134 2
        foreach ($oldest_dm as $attribute => $oldest_value) {
135 2
            if (is_array($oldest_value)) {
136
                continue;
137
            }
138
139
            $entry = [
140 2
                'old' => $oldest_value,
141 2
                'new' => $newest_dm[$attribute] ?? ''
142
            ];
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 midcom_services_rcs_renderer_html_sidebyside([
149 2
                    'old' => $oldest_version,
150 2
                    'new' => $latest_version
151
                ]);
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