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
|
|
|
|