1 | <?php |
||||
2 | /** |
||||
3 | * EGgroupware admin - New et2 site configuration |
||||
4 | * |
||||
5 | * @link http://www.egroupware.org |
||||
6 | * @author Ralf Becker <[email protected]> |
||||
7 | * @package admin |
||||
8 | * @copyright (c) 2016-18 by Ralf Becker <[email protected]> |
||||
9 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||
10 | */ |
||||
11 | |||||
12 | use EGroupware\Api; |
||||
13 | |||||
14 | /** |
||||
15 | * New site configuration for all apps using eTemplate2 $app/templates/default/config.xet |
||||
16 | */ |
||||
17 | class admin_config |
||||
18 | { |
||||
19 | var $public_functions = array('index' => True); |
||||
20 | |||||
21 | /** |
||||
22 | * Upload function to store anonymous images into instance files_dir/anon_images |
||||
23 | * |
||||
24 | * @param array $file file info array |
||||
25 | * @param array|string $value current value of login_background_file |
||||
26 | * |
||||
27 | */ |
||||
28 | function ajax_upload_anon_images ($file, $value) |
||||
29 | { |
||||
30 | if (!isset($GLOBALS['egw_info']['user']['apps']['admin'])) die('no rights to be here!'); |
||||
0 ignored issues
–
show
|
|||||
31 | $path = $GLOBALS['egw_info']['server']['files_dir'].'/anon-images'; |
||||
32 | $success = false; |
||||
33 | $response = Api\Json\Response::get(); |
||||
34 | if (is_array($file) && is_writable(dirname($path))) |
||||
35 | { |
||||
36 | if (!is_dir($path)) mkdir ($path); |
||||
37 | $tmp_file = array_keys($file); |
||||
38 | $destination = $path.'/'.$file[$tmp_file[0]]['name']; |
||||
39 | $success = rename($GLOBALS['egw_info']['server']['temp_dir'].'/'.$tmp_file[0],$destination); |
||||
40 | } |
||||
41 | if ($success) |
||||
42 | { |
||||
43 | $value = array_merge($value, array( |
||||
44 | $GLOBALS['egw_info']['server']['webserver_url'].'/api/anon_images.php?src='.urlencode($file[$tmp_file[0]]['name']).'&'.filemtime($destination), |
||||
45 | )); |
||||
46 | $response->data($value); |
||||
47 | } |
||||
48 | else |
||||
49 | { |
||||
50 | $response->error(lang('Failed to upload %1',$destination)); |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $destination .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
51 | } |
||||
52 | } |
||||
53 | |||||
54 | /** |
||||
55 | * Removes images from anon-images directory |
||||
56 | * |
||||
57 | * @param type $files |
||||
58 | */ |
||||
59 | function remove_anon_images ($files) |
||||
60 | { |
||||
61 | if (!isset($GLOBALS['egw_info']['user']['apps']['admin'])) die('no rights to be here!'); |
||||
0 ignored issues
–
show
|
|||||
62 | if ($files) |
||||
0 ignored issues
–
show
|
|||||
63 | { |
||||
64 | $base = $GLOBALS['egw_info']['server']['files_dir'].'/anon-images'; |
||||
65 | foreach ($files as $file) |
||||
66 | { |
||||
67 | $parts2 = explode('anon_images.php?src=', $file); |
||||
68 | $parts = explode('&', $parts2[1]); |
||||
69 | $path = $base.'/'.urldecode($parts[0]); |
||||
70 | if (is_writable(dirname($base)) && file_exists($path)) |
||||
71 | { |
||||
72 | unlink($path); |
||||
73 | } |
||||
74 | } |
||||
75 | } |
||||
76 | } |
||||
77 | |||||
78 | /** |
||||
79 | * List of configs |
||||
80 | * |
||||
81 | * @param type $_content |
||||
82 | * @throws Api\Exception\WrongParameter |
||||
83 | */ |
||||
84 | function index($_content=null) |
||||
85 | { |
||||
86 | if (is_array($_content)) |
||||
0 ignored issues
–
show
|
|||||
87 | { |
||||
88 | $_appname = $_content['appname']; |
||||
89 | } |
||||
90 | elseif (!empty($_GET['appname']) && isset($GLOBALS['egw_info']['apps'][$_GET['appname']])) |
||||
91 | { |
||||
92 | $_appname = $_GET['appname']; |
||||
93 | } |
||||
94 | else |
||||
95 | { |
||||
96 | throw new Api\Exception\WrongParameter("Wrong or missing appname parameter!"); |
||||
97 | } |
||||
98 | if ($GLOBALS['egw']->acl->check('site_config_acce',1,'admin')) |
||||
99 | { |
||||
100 | Api\Framework::redirect_link('/index.php'); |
||||
101 | } |
||||
102 | |||||
103 | // load the translations of the app we show too, so they dont need to be in admin! |
||||
104 | if ($_appname != 'admin') |
||||
105 | { |
||||
106 | Api\Translation::add_app($_appname); |
||||
107 | } |
||||
108 | |||||
109 | switch($_appname) |
||||
110 | { |
||||
111 | case 'admin': |
||||
112 | case 'addressbook': |
||||
113 | case 'calendar': |
||||
114 | case 'preferences': |
||||
115 | $appname = $_appname; |
||||
116 | $config_appname = 'phpgwapi'; |
||||
117 | break; |
||||
118 | case 'phpgwapi': |
||||
119 | case '': |
||||
120 | /* This keeps the admin from getting into what is a setup-only config */ |
||||
121 | Api\Framework::redirect_link('/admin/index.php?ajax=true'); |
||||
122 | break; |
||||
123 | default: |
||||
124 | $appname = $_appname; |
||||
125 | $config_appname = $appname; |
||||
126 | break; |
||||
127 | } |
||||
128 | |||||
129 | $c = new Api\Config($config_appname); |
||||
130 | $old = (array)$c->read_repository(); |
||||
131 | if ($_content['cancel'] || ($_content['save'] || $_content['apply']) && $GLOBALS['egw']->acl->check('site_config_acce',2,'admin')) |
||||
132 | { |
||||
133 | Api\Framework::redirect_link('/admin/index.php?ajax=true'); |
||||
134 | } |
||||
135 | |||||
136 | if ($_content['save'] || $_content['apply']) |
||||
137 | { |
||||
138 | // support old validation hooks |
||||
139 | $_POST = array('newsettings' => &$_content['newsettings']); |
||||
140 | |||||
141 | // Remove actual files (cleanup) of deselected urls from login_background_file |
||||
142 | if (!empty($c->config_data['login_background_file'])) |
||||
0 ignored issues
–
show
The property
EGroupware\Api\Config::$config_data has been deprecated: dont use direct
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This property has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead. ![]() |
|||||
143 | { |
||||
144 | $this->remove_anon_images(array_diff((array)$c->config_data['login_background_file'], (array)$_content['newsettings']['login_background_file'])); |
||||
0 ignored issues
–
show
The property
EGroupware\Api\Config::$config_data has been deprecated: dont use direct
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This property has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead. ![]() |
|||||
145 | } |
||||
146 | |||||
147 | /* Load hook file with functions to validate each config (one/none/all) */ |
||||
148 | $errors = Api\Hooks::single(array( |
||||
149 | 'location' => 'config_validate', |
||||
150 | )+(array)$_content['newsettings'], $appname); |
||||
151 | if (!is_string($errors)) $errors = ''; // old hooks allways return true |
||||
152 | |||||
153 | foreach($_content['newsettings'] as $key => $config) |
||||
154 | { |
||||
155 | if ($config) |
||||
156 | { |
||||
157 | $c->config_data[$key] = $config; |
||||
0 ignored issues
–
show
The property
EGroupware\Api\Config::$config_data has been deprecated: dont use direct
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This property has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead. ![]() |
|||||
158 | if (in_array($key, (array)$GLOBALS['egw_info']['server']['found_validation_hook'], true) && function_exists($key)) |
||||
159 | { |
||||
160 | call_user_func($key, $config, $c); |
||||
161 | if($GLOBALS['config_error']) |
||||
162 | { |
||||
163 | $errors .= lang($GLOBALS['config_error']) . "\n"; |
||||
164 | $GLOBALS['config_error'] = False; |
||||
165 | } |
||||
166 | } |
||||
167 | } |
||||
168 | // don't erase passwords, since we also don't print them |
||||
169 | elseif(strpos($key,'passwd') === false && strpos($key,'password') === false && strpos($key,'root_pw') === false) |
||||
170 | { |
||||
171 | unset($c->config_data[$key]); |
||||
0 ignored issues
–
show
The property
EGroupware\Api\Config::$config_data has been deprecated: dont use direct
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This property has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead. ![]() |
|||||
172 | } |
||||
173 | } |
||||
174 | if(in_array('final_validation', (array)$GLOBALS['egw_info']['server']['found_validation_hook']) && |
||||
175 | function_exists('final_validation')) |
||||
176 | { |
||||
177 | final_validation($_content['newsettings']); |
||||
178 | if($GLOBALS['config_error']) |
||||
179 | { |
||||
180 | $errors .= lang($GLOBALS['config_error']) . "\n"; |
||||
181 | $GLOBALS['config_error'] = False; |
||||
182 | } |
||||
183 | unset($GLOBALS['egw_info']['server']['found_validation_hook']); |
||||
184 | } |
||||
185 | |||||
186 | // do not allow to save config, if there are errors |
||||
187 | if (!$errors) |
||||
188 | { |
||||
189 | // compute real changes and their old values (null for removals) |
||||
190 | $modifications = array_udiff_assoc($c->config_data, $old, function($a, $b) |
||||
0 ignored issues
–
show
The property
EGroupware\Api\Config::$config_data has been deprecated: dont use direct
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This property has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead. ![]() |
|||||
191 | { |
||||
192 | return (int)($a != $b); // necessary to kope with arrays |
||||
193 | }); |
||||
194 | $removals = array_diff(array_udiff_assoc($old, $c->config_data, function($a, $b) |
||||
0 ignored issues
–
show
The property
EGroupware\Api\Config::$config_data has been deprecated: dont use direct
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This property has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead. ![]() |
|||||
195 | { |
||||
196 | return (int)(!empty($a) && $a != $b); |
||||
197 | }), array(null, '')); |
||||
198 | $set = array_merge(array_fill_keys(array_keys($removals), null), $modifications); |
||||
199 | $old = array_filter($old, function($key) use ($set) |
||||
200 | { |
||||
201 | return array_key_exists($key, $set); |
||||
202 | }, ARRAY_FILTER_USE_KEY); |
||||
203 | if ($set) |
||||
0 ignored issues
–
show
The expression
$set of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||||
204 | { |
||||
205 | $cmd = new admin_cmd_config($_appname, $set, $old, |
||||
206 | (array)$_content['admin_cmd'], $config_appname === 'phpgwapi'); |
||||
207 | $msg = $cmd->run(); |
||||
208 | } |
||||
209 | else |
||||
210 | { |
||||
211 | $msg = lang('Nothing to save.'); |
||||
212 | } |
||||
213 | // allow apps to hook into configuration dialog to eg. save some stuff outside configuration |
||||
214 | $_content['location'] = 'config_after_save'; |
||||
215 | Api\Hooks::single($_content, $_appname); |
||||
216 | } |
||||
217 | if(!$errors && !$_content['apply']) |
||||
218 | { |
||||
219 | Api\Framework::message($msg, 'success'); |
||||
220 | Api\Framework::redirect_link('/index.php', array( |
||||
221 | 'menuaction' => 'admin.admin_ui.index', |
||||
222 | 'ajax' => 'true' |
||||
223 | ), 'admin'); |
||||
224 | } |
||||
225 | } |
||||
226 | |||||
227 | if($errors) |
||||
228 | { |
||||
229 | Api\Framework::message(lang('Error') . ': ' . $errors, 'error'); |
||||
230 | unset($errors); |
||||
231 | unset($GLOBALS['config_error']); |
||||
232 | |||||
233 | // keep old content on error |
||||
234 | $config = $_content['newsettings']; |
||||
235 | } |
||||
236 | else |
||||
237 | { |
||||
238 | if ($_content['apply']) |
||||
239 | { |
||||
240 | Api\Framework::message($msg, 'success'); |
||||
241 | } |
||||
242 | $config = $c->read_repository(); |
||||
243 | } |
||||
244 | $sel_options = $readonlys = array(); |
||||
245 | // call "config" hook, allowing apps to overwrite config, eg. set default values, |
||||
246 | // or return options in "sel_options" keys |
||||
247 | $config['location'] = 'config'; |
||||
248 | // let config hook know, this is an inital call and not one after user hits [Apply] button |
||||
249 | $config['initial-call'] = is_null($_content); |
||||
250 | $ret = Api\Hooks::single($config, $appname); |
||||
251 | if (is_array($ret)) |
||||
252 | { |
||||
253 | if (isset($ret['sel_options'])) $sel_options = $ret['sel_options']; |
||||
254 | $config = array_merge($config, $ret); |
||||
255 | } |
||||
256 | |||||
257 | $tmpl = new Api\Etemplate($appname.'.config'); |
||||
258 | $path = (parse_url($tmpl->rel_path, PHP_URL_SCHEME) !== 'vfs' ? EGW_SERVER_ROOT : '').$tmpl->rel_path; |
||||
259 | $content = array( |
||||
260 | 'tabs' => $_content['tabs'], |
||||
261 | 'tabs2' => $_content['tabs2'], |
||||
262 | 'template' => $appname.'.config', |
||||
263 | 'newsettings' => array(), |
||||
264 | ); |
||||
265 | |||||
266 | // for security reasons we do not send all config to client-side, but only ones mentioned in templates |
||||
267 | $matches = null; |
||||
268 | preg_match_all('/id="newsettings\[([^]]+)\]/', file_get_contents($path), $matches, PREG_PATTERN_ORDER); |
||||
269 | foreach($matches[1] as $name) |
||||
270 | { |
||||
271 | $content['newsettings'][$name] = isset($config[$name]) ? $config[$name] : ''; |
||||
272 | } |
||||
273 | |||||
274 | // make everything readonly and remove save/apply button, if user has not rights to store config |
||||
275 | if ($GLOBALS['egw']->acl->check('site_config_acce',2,'admin')) |
||||
276 | { |
||||
277 | $readonlys[__ALL__] = true; |
||||
0 ignored issues
–
show
|
|||||
278 | $readonlys['cancel'] = false; |
||||
279 | } |
||||
280 | |||||
281 | $tmpl->read('admin.site-config'); |
||||
282 | $method = (get_called_class() == __CLASS__) ? 'admin.admin_config.index' : "$appname.".get_called_class().'.'.__FUNCTION__; |
||||
283 | $tmpl->exec($method, $content, $sel_options, $readonlys, array('appname' => $appname)); |
||||
284 | } |
||||
285 | } |
||||
286 |
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.