Textpattern | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * https://textpattern.com/ 6 * 7 * Copyright (C) 2020 The Textpattern Development Team 8 * 9 * This file is part of Textpattern. 10 * 11 * Textpattern is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation, version 2. 14 * 15 * Textpattern is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Textpattern. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 function setup_db($cfg = array()) 25 { 26 global $txpcfg, $DB, $prefs, $txp_user, $txp_groups; 27 global $permlink_mode, $siteurl, $adminurl, $theme_name, $public_themes, $step; 28 include_once txpath.'/lib/txplib_db.php'; 29 include_once txpath.'/lib/admin_config.php'; 30 31 $siteurl = rtrim(@$cfg['site']['public_url'], '/'); 32 33 if (!preg_match('%^https?://%', $siteurl)) { 34 $siteurl = 'http://'.$siteurl; 35 } 36 37 if (empty($cfg['site']['admin_url'])) { 38 $adminurl = $siteurl.'/textpattern'; 39 } else { 40 $adminurl = rtrim(@$cfg['site']['admin_url'], '/'); 41 42 if (!preg_match('%^https?://%', $adminurl)) { 43 $adminurl = 'http://'.$adminurl; 44 } 45 } 46 47 // Determine the mode of permanent links. 48 ini_set('default_socket_timeout', 10); 49 $s = md5(uniqid(rand(), true)); 50 $pretext_data = @file("{$siteurl}/{$s}/?txpcleantest=1"); 51 52 if (trim(@$pretext_data[0]) == md5("/{$s}/?txpcleantest=1")) { 53 $permlink_mode = 'section_title'; 54 } else { 55 $permlink_mode = 'messy'; 56 } 57 58 // Variable set. 59 @define('hu', $siteurl.'/'); 60 $siteurl = preg_replace('%^https?://%', '', $siteurl); 61 $siteurl = str_replace(' ', '%20', $siteurl); 62 $theme_name = empty($cfg['site']['admin_theme']) ? 'hive' : $cfg['site']['admin_theme']; 63 64 $Skin = Txp::get('Textpattern\Skin\Skin'); 65 66 $setup_path = txpath.DS.'setup'; 67 $setup_themes_path = $setup_path.DS.'themes'; 68 $Skin->setDirPath($setup_themes_path); 69 $setup_public_themes = $Skin->getUploaded(); 70 71 $root_themes_path = txpath.DS.'..'.DS.'themes'; 72 $Skin->setDirPath($root_themes_path); 73 $root_public_themes = $Skin->getUploaded(); 74 75 $public_themes = array_merge($root_public_themes, $setup_public_themes); 76 77 if (empty($cfg['site']['public_theme']) || !array_key_exists($cfg['site']['public_theme'], $public_themes)) { 78 $public_theme = current(array_keys($public_themes)); 79 } else { 80 $public_theme = $cfg['site']['public_theme']; 81 } 82 83 $is_from_setup = in_array($public_theme, array_keys($setup_public_themes)); 84 85 if (empty($cfg['site']['content_directory'])) { 86 /* Option 'txp-data' in manifest.json: 87 <default> - Import /articles and /data from setup dir 88 txp-data == 'theme' - Import /articles and /data from theme dir 89 txp-data == 'none' - Nothing to import. 90 */ 91 92 if (@$public_themes[$public_theme]['txp-data'] == 'theme') { 93 $datadir = $is_from_setup ? $setup_themes_path.DS.$public_theme : $root_public_themes.DS.$public_theme; 94 } elseif (@$public_themes[$public_theme]['txp-data'] == 'none') { 95 $datadir = ''; 96 } else { 97 $datadir = $setup_path; 98 } 99 } else { 100 $datadir = $cfg['site']['content_directory']; 101 } 102 103 if (numRows(safe_query("SHOW TABLES LIKE '".PFX."textpattern'"))) { 104 if (! empty($step)) { 105 $step = 'step_printConfig'; 106 echo txp_setup_progress_meter(4).n.'<div class="txp-setup">'; 107 } 108 109 msg(gTxt('tables_exist', array('{dbname}' => @$txpcfg['db'])), MSG_ERROR, true); 110 } 111 112 $setup = new \Textpattern\DB\Core(); 113 114 // Create tables 115 $setup->createAllTables(); 116 117 // Initial mandatory data 118 $setup->initData(); 119 msg(gTxt('creating_db_tables')); 120 121 setup_txp_lang($cfg['site']['language_code']); 122 123 // Create core prefs 124 $setup->initPrefs(); 125 126 $prefs = get_prefs(); 127 $txp_user = $cfg['user']['login_name']; 128 129 create_user($txp_user, $cfg['user']['email'], $cfg['user']['password'], $cfg['user']['full_name'], 1); 130 131 if ($datadir) { 132 /* Load theme prefs: 133 /data/core.prefs - Allow override some core prefs. Used only in setup theme. 134 /data/theme.prefs - Theme global and private prefs. 135 global - Used in setup and for AutoCreate missing prefs. 136 private - Will be created after user login 137 */ 138 foreach (get_files_content($datadir.'/data', 'prefs') as $key => $data) { 139 if ($out = @json_decode($data, true)) { 140 msg("Prefs: 'data/{$key}'"); 141 142 foreach ($out as $name => $p) { 143 if (empty($p['private'])) { 144 @set_pref($name, $p['val'], $p['event'], $p['type'], $p['html'], $p['position']); 145 } 146 } 147 } 148 } 149 150 $prefs = get_prefs(); 151 $plugin = new \Textpattern\Plugin\Plugin(); 152 153 foreach (get_files_content($datadir.'/plugin', 'txt') as $key => $data) { 154 $result = $plugin->install($data, 1); 155 msg("Plugin: '{$key}' - ".(is_array($result) ? $result[0] : $result)); 156 } 157 158 $import = new \Textpattern\Import\TxpXML(); 159 160 foreach (get_files_content($datadir.'/data', 'xml') as $key => $data) { 161 $import->importXml($data); 162 msg("Import: 'data/{$key}'"); 163 } 164 165 foreach (get_files_content($datadir.'/articles', 'xml') as $key => $data) { 166 $import->importXml($data); 167 msg("Import: 'articles/{$key}'"); 168 } 169 } 170 171 // --- Theme setup. 172 // Import theme assets. 173 msg(gTxt('public_theme').": '{$public_theme}'"); 174 $is_from_setup ? $Skin->setDirPath($setup_themes_path) : ''; 175 $Skin->setNames(array($public_theme)) 176 ->import() 177 ->setBase('default') 178 ->updateSections(); 179 180 // --- Theme setup end 181 182 // Final rebuild category trees 183 rebuild_tree_full('article'); 184 rebuild_tree_full('link'); 185 rebuild_tree_full('image'); 186 rebuild_tree_full('file'); 187 } 188 189 /** 190 * Installing Language Files 191 * 192 * @param string $langs The desired language code or comma separated string 193 */ 194 195 function setup_txp_lang($langs) 196 { 197 global $language; 198 199 Txp::getContainer()->remove('\Textpattern\L10n\Lang'); 200 201 $langs = array_flip(do_list_unique($langs)); 202 unset($langs[$language]); 203 204 if (!Txp::get('\Textpattern\L10n\Lang')->installFile($language)) { 205 // If cannot install from lang file, setup the Default lang. `language` pref changed too. 206 $language = TEXTPATTERN_DEFAULT_LANG; 207 Txp::get('\Textpattern\L10n\Lang')->installFile($language); 208 unset($langs[$language]); 209 } 210 211 msg("Lang: '{$language}'"); 212 213 foreach (array_flip($langs) as $lang) { 214 Txp::get('\Textpattern\L10n\Lang')->installFile($lang); 215 msg("Lang: '{$lang}'"); 216 } 217 } 218 219 /** 220 * Merge the desired lang strings with fallbacks. 221 * 222 * The fallback language is guaranteed to exist, so any unknown strings 223 * will be used from that pack to fill in any gaps. 224 * 225 * @param string $langs The desired language code or comma separated string 226 * @return array The language-specific name-value pairs 227 */ 228 229 function setup_load_lang($langs) 230 { 231 global $language; 232 233 $langs = do_list($langs); 234 $lang = $langs[0]; 235 $lang_path = txpath.DS.'setup'.DS.'lang'.DS; 236 $group = 'common, setup'; 237 $strings = array(); 238 239 // Load the desired lang strings and default strings as fallbacks. 240 $default_textpack = Txp::get('\Textpattern\L10n\Lang', $lang_path)->getPack(TEXTPATTERN_DEFAULT_LANG, $group); 241 $lang_textpack = Txp::get('\Textpattern\L10n\Lang', $lang_path)->getPack($lang, $group); 242 243 $language = empty($lang_textpack) ? TEXTPATTERN_DEFAULT_LANG : $lang; 244 @define('LANG', $language); 245 246 $allStrings = array_merge($default_textpack, $lang_textpack); 247 248 // Merge the arrays, using the default language to fill in the blanks. 249 foreach ($allStrings as $meta) { 250 if (empty($strings[$meta['name']])) { 251 $strings[$meta['name']] = $meta['data']; 252 } 253 } 254 255 Txp::get('\Textpattern\L10n\Lang')->setPack($strings) 256 ->setPack(array('help' => gTxt('setup_help')), true); 257 258 return $strings; 259 } 260 261 /** 262 * Generate a config.php file from the known info. 263 */ 264 265 function setup_makeConfig($cfg, $doSpecial = false) 266 { 267 define("nl", "';\n"); 268 define("o", '$txpcfg[\''); 269 define("m", "'] = '"); 270 271 // Escape single quotes and backslashes in literal PHP strings. 272 foreach ($cfg['database'] as $k => $v) { 273 $cfg['database'][$k] = addcslashes($cfg['database'][$k], "'\\"); 274 } 275 276 if ($doSpecial) { 277 $cfg['database'] = doSpecial($cfg['database']); 278 } 279 280 $config_details = 281 "<"."?php\n" 282 .o.'db'.m.$cfg['database']['db_name'].nl 283 .o.'user'.m.$cfg['database']['user'].nl 284 .o.'pass'.m.$cfg['database']['password'].nl 285 .o.'host'.m.$cfg['database']['host'].nl 286 .(empty($cfg['database']['client_flags']) ? '' : o.'client_flags'."'] = ".$cfg['database']['client_flags'].";\n") 287 .o.'table_prefix'.m.$cfg['database']['table_prefix'].nl 288 .o.'txpath'.m.txpath.nl 289 .o.'dbcharset'.m.$cfg['database']['charset'].nl; 290 291 if (defined('is_multisite')) { 292 $config_details .= 293 o.'multisite_root_path'.m.multisite_root_path.nl 294 .o.'admin_url'.m.$cfg['site']['admin_url'].nl 295 .o.'cookie_domain'.m.$cfg['site']['cookie_domain'].nl 296 .'if (!defined(\'txpath\')) { define(\'txpath\', $txpcfg[\'txpath\']); }'."\n"; 297 } 298 299 $config_details .= 300 "// For more customization options, please consult config-dist.php file."; 301 302 return $config_details; 303 } 304 305 /** 306 * Try to connect to the database. 307 * 308 * Check the database and the table prefix. 309 */ 310 311 function setup_connect() 312 { 313 global $cfg; 314 315 if (strpos($cfg['database']['host'], ':') === false) { 316 $dhost = $cfg['database']['host']; 317 $dport = ini_get("mysqli.default_port"); 318 } else { 319 list($dhost, $dport) = explode(':', $cfg['database']['host'], 2); 320 $dport = intval($dport); 321 } 322 323 $dsocket = ini_get("mysqli.default_socket"); 324 325 $mylink = mysqli_init(); 326 327 if (@mysqli_real_connect($mylink, $dhost, $cfg['database']['user'], $cfg['database']['password'], '', $dport, $dsocket)) { 328 $cfg['database']['client_flags'] = 0; 329 } elseif (@mysqli_real_connect($mylink, $dhost, $cfg['database']['user'], $cfg['database']['password'], '', $dport, $dsocket, MYSQLI_CLIENT_SSL)) { 330 $cfg['database']['client_flags'] = 'MYSQLI_CLIENT_SSL'; 331 } else { 332 msg(gTxt('db_cant_connect'), MSG_ERROR, true); 333 } 334 335 echo msg(gTxt('db_connected')); 336 337 if (!($cfg['database']['table_prefix'] == '' || preg_match('#^[a-zA-Z_][a-zA-Z0-9_]*$#', $cfg['database']['table_prefix']))) { 338 msg(gTxt('prefix_bad_characters', 339 array('{dbprefix}' => strong(txpspecialchars($cfg['database']['table_prefix']))), 'raw'), 340 MSG_ERROR, true 341 ); 342 } 343 344 if (!$mydb = mysqli_select_db($mylink, $cfg['database']['db_name'])) { 345 msg(gTxt('db_doesnt_exist', 346 array('{dbname}' => strong(txpspecialchars($cfg['database']['db_name']))), 'raw'), 347 MSG_ERROR, true 348 ); 349 } 350 351 $tables_exist = mysqli_query($mylink, "DESCRIBE `".$cfg['database']['table_prefix']."textpattern`"); 352 if ($tables_exist) { 353 msg(gTxt('tables_exist', 354 array('{dbname}' => strong(txpspecialchars($cfg['database']['db_name']))), 'raw'), 355 MSG_ERROR, true 356 ); 357 } 358 359 // On MySQL 5.5.3+ use real UTF-8 tables, if the client supports it. 360 $cfg['database']['charset'] = "utf8mb4"; 361 362 // Lower versions only support UTF-8 limited to 3 bytes per character 363 if (mysqli_get_server_version($mylink) < 50503) { 364 $cfg['database']['charset'] = "utf8"; 365 } else { 366 if (false !== strpos(mysqli_get_client_info($mylink), 'mysqlnd')) { 367 // mysqlnd 5.0.9+ required 368 if (mysqli_get_client_version($mylink) < 50009) { 369 $cfg['database']['charset'] = "utf8"; 370 } 371 } else { 372 // libmysqlclient 5.5.3+ required 373 if (mysqli_get_client_version($mylink) < 50503) { 374 $cfg['database']['charset'] = "utf8"; 375 } 376 } 377 } 378 @mysqli_close($mylink); 379 echo msg(gTxt('using_db', array( 380 '{dbname}' => strong(txpspecialchars($cfg['database']['db_name'])), ), 'raw').' <bdi dir="ltr">('.$cfg['database']['charset'].')</bdi>'); 381 382 return true; 383 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title