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