From cef4b4de401511a4195b7b8ad04b47bec1895b20 Mon Sep 17 00:00:00 2001 From: Janusz Dobrowolski Date: Sat, 17 Jul 2010 16:31:47 +0000 Subject: [PATCH] Support for new packaged extensions system. --- FA.pem | 17 + admin/db/maintenance_db.inc | 104 +++--- admin/inst_lang.php | 321 +++++++++-------- admin/inst_module.php | 266 +++++++------- config.default.php | 10 + frontaccounting.php | 50 +-- includes/access_levels.inc | 2 +- includes/archive.inc | 466 +++++++++++++++++++++++++ includes/packages.inc | 647 +++++++++++++++++++++++++++++++++++ includes/ui/ui_controls.inc | 1 - includes/ui/ui_view.inc | 13 + includes/ui/view_package.php | 50 +++ modules/_cache/index.php | 3 + 13 files changed, 1601 insertions(+), 349 deletions(-) create mode 100644 FA.pem create mode 100644 includes/archive.inc create mode 100644 includes/packages.inc create mode 100644 includes/ui/view_package.php create mode 100644 modules/_cache/index.php diff --git a/FA.pem b/FA.pem new file mode 100644 index 00000000..b51aae2f --- /dev/null +++ b/FA.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICyzCCAjSgAwIBAgIJAPmoLn2B45+EMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNV +BAYTAlVTMREwDwYDVQQIEwhEZWxhd2FyZTEOMAwGA1UEBxMFRG92ZXIxHDAaBgNV +BAMTE2Zyb250YWNjb3VudGluZy5uZXQwHhcNMDkxMTE2MTIxNjEwWhcNMTAxMTE2 +MTIxNjEwWjBOMQswCQYDVQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxDjAMBgNV +BAcTBURvdmVyMRwwGgYDVQQDExNmcm9udGFjY291bnRpbmcubmV0MIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQDm9BbI0KCUrzOQPtZkNpI0mBvnSIjgIAIZXPgB +UQYqRpNihfrA3eIkpH87X6/zPEmoZXIDrzlXFYzLRvlMwHOnPbVeR0Z3HTT4AYYU ++lcL0l4vIJfaNbRjGXSJ8w/FDc4XOMBU5ut5+zPJmwrqmJsyMcIkN0JX8/5g4+lk +xTnpIQIDAQABo4GwMIGtMB0GA1UdDgQWBBRzT6vxvlhlrXDXTT/WEBZA7k8xMjB+ +BgNVHSMEdzB1gBRzT6vxvlhlrXDXTT/WEBZA7k8xMqFSpFAwTjELMAkGA1UEBhMC +VVMxETAPBgNVBAgTCERlbGF3YXJlMQ4wDAYDVQQHEwVEb3ZlcjEcMBoGA1UEAxMT +ZnJvbnRhY2NvdW50aW5nLm5ldIIJAPmoLn2B45+EMAwGA1UdEwQFMAMBAf8wDQYJ +KoZIhvcNAQEFBQADgYEA4CJMjO0lR5Vsz3N64MBSMpZKw2XgmZDFgMOORiJ9btJA +EAnc5mYYsCmZFlXVp1XOj3hcoggrMPmgyLLbagAz5NW2wgEg4xzyE9PhWDJptkww +oW+J9NyESy8pIsZG/FH0FgoFYshrRRGaK0pImNOSj5HUThfBJLJpLbkm2J1aHag= +-----END CERTIFICATE----- diff --git a/admin/db/maintenance_db.inc b/admin/db/maintenance_db.inc index 958572c2..11914e02 100644 --- a/admin/db/maintenance_db.inc +++ b/admin/db/maintenance_db.inc @@ -82,25 +82,8 @@ function write_config_db($new = false) $msg .= "\$def_coy = " . $def_coy . ";\n\n"; $msg .= "\$tb_pref_counter = " . $tb_pref_counter . ";\n\n"; - $msg .= "\$db_connections = array (\n"; - $msg .= "\t0 => "; - for ($i = 0; $i < $n; $i++) - { - if ($i > 0) - $msg .= "\tarray "; - else - $msg .= "array "; - $msg .= "('name' => '" . $db_connections[$i]['name'] . "',\n"; - $msg .= "\t\t'host' => '" . $db_connections[$i]['host'] . "',\n"; - $msg .= "\t\t'dbuser' => '" . $db_connections[$i]['dbuser'] . "',\n"; - $msg .= "\t\t'dbpassword' => '" . $db_connections[$i]['dbpassword'] . "',\n"; - $msg .= "\t\t'dbname' => '" . $db_connections[$i]['dbname'] . "',\n"; - $msg .= "\t\t'tbpref' => '" . $db_connections[$i]['tbpref'] . "')"; - if ($i != $n - 1) - $msg .= ","; - $msg .= "\n\n"; - } - $msg .= "\t);\n?>"; + $msg .= "\$db_connections = " .var_export($db_connections, true); + $msg .= ";\n?>"; $filename = $path_to_root . "/config_db.php"; // Check if the file exists and is writable first. @@ -167,20 +150,8 @@ function write_extensions($extensions=null, $company = -1) by extensions editor. */\n\n"; - $msg .= "\$installed_extensions = array (\n"; - foreach($extensions as $i => $ext) - { - $msg .= "\t$i => "; - $msg .= "array ( "; - $t = ''; - foreach($ext as $key => $val) { - $msg .= $t."'$key' => '$val',\n"; - $t = "\t\t\t"; - } - $msg .= "\t\t),\n"; - } - $msg .= "\t);\n?>"; - + $msg .= "\$installed_extensions = ". var_export($extensions, true); + $msg .= ";\n?>"; $filename = $path_to_root . ($company==-1 ? '' : '/company/'.$company) .'/installed_extensions.php'; @@ -205,43 +176,57 @@ function write_extensions($extensions=null, $company = -1) } return true; } +//--------------------------------------------------------------------------------------------- +// +// Update per-company list of installed extensions +// +function update_extensions($extensions) { + global $db_connections; + + if (!write_extensions($extensions)) { + display_notification(_("Cannot update system extensions list.")); + return false; + } + + // update per company files + $cnt = count($db_connections); + for($i = 0; $i < $cnt; $i++) + { + $newexts = $extensions; + // update 'active' status + $exts = get_company_extensions($i); + foreach ($exts as $key => $ext) + { + if (isset($newexts[$key])) + $newexts[$key]['active'] = $exts[$key]['active']; + } + if(!write_extensions($newexts, $i)) + { + display_notification(sprintf(_("Cannot update extensions list for company '%s'."), + $db_connections[$i]['name'])); + return false; + } + } + return true; +} + function write_lang() { global $path_to_root, $installed_languages, $dflt_lang; - $conn = array_natsort($installed_languages, 'code', 'code'); - $installed_languages = $conn; - //reset($installed_languages); + $installed_languages = array_natsort($installed_languages, 'code', 'code'); $n = count($installed_languages); $msg = " ')\n"; $msg .= "-- 'code' should match the name of the directory for the language under \\lang\n"; $msg .= "-- 'name' is the name that will be displayed in the language selection list (in Users and Display Setup)\n"; $msg .= "-- 'rtl' only needs to be set for right-to-left languages like Arabic and Hebrew\n\n"; $msg .= "*/\n\n\n"; - $msg .= "\$installed_languages = array (\n"; - if ($n > 0) - $msg .= "\t0 => "; - for ($i = 0; $i < $n; $i++) - { - if ($i > 0) - $msg .= "\t\tarray "; - else - $msg .= "array "; - $msg .= "('code' => '" . $installed_languages[$i]['code'] . "', "; - $msg .= "'name' => '" . $installed_languages[$i]['name'] . "', "; - $msg .= "'encoding' => '" . $installed_languages[$i]['encoding'] . "'"; - if (isset($installed_languages[$i]['rtl']) && $installed_languages[$i]['rtl']) - $msg .= ", 'rtl' => true),\n"; - else - $msg .= "),\n"; - } - - $msg .= "\t);\n"; + $msg .= "\$installed_languages = " . var_export($installed_languages, true); + $msg .= ";\n"; $msg .= "\n\$dflt_lang = '$dflt_lang';\n?>\n"; $path = $path_to_root . "/lang"; @@ -316,7 +301,7 @@ function db_drop_db($connection) function db_import($filename, $connection, $force=true) { global $db, $go_debug; - + $allowed_commands = array( "create" => 'table_queries', "alter table" => 'table_queries', @@ -338,7 +323,6 @@ function db_import($filename, $connection, $force=true) ini_set("max_execution_time", "180"); db_query("SET foreign_key_checks=0"); - // uncrompress gziped backup files if (strpos($filename, ".gz") || strpos($filename, ".GZ")) $lines = db_ungzip("lines", $filename); @@ -604,8 +588,8 @@ function db_export($conn, $filename, $zip='no', $comment='', $tbpref = TB_PREF) $out.=$table_sql[$tablename]; // add auto_increment value - if ($auto_incr[$tablename]) - $out.=" AUTO_INCREMENT=".$auto_incr[$tablename]; +// if ($auto_incr[$tablename]) +// $out.=" AUTO_INCREMENT=".$auto_incr[$tablename]; $out.=" ;"; $out.="\n\n\n"; diff --git a/admin/inst_lang.php b/admin/inst_lang.php index 858e13a6..54183e09 100644 --- a/admin/inst_lang.php +++ b/admin/inst_lang.php @@ -13,47 +13,138 @@ $page_security = 'SA_CREATELANGUAGE'; $path_to_root=".."; include_once($path_to_root . "/includes/session.inc"); -page(_($help_context = "Install/Update Languages")); - -include_once($path_to_root . "/includes/date_functions.inc"); -include_once($path_to_root . "/admin/db/company_db.inc"); +include_once($path_to_root."/includes/packages.inc"); include_once($path_to_root . "/admin/db/maintenance_db.inc"); include_once($path_to_root . "/includes/ui.inc"); -//--------------------------------------------------------------------------------------------- +if ($use_popup_windows) + $js = get_js_open_window(900, 500); -if (isset($_GET['selected_id'])) -{ - $selected_id = $_GET['selected_id']; -} -elseif (isset($_POST['selected_id'])) -{ - $selected_id = $_POST['selected_id']; -} -else - $selected_id = -1; +page(_($help_context = "Install/Update Languages"), false, false, "", $js); + +simple_page_mode(true); //--------------------------------------------------------------------------------------------- +// +// Display all packages - both already installed and available from repository +// +function display_languages() +{ + global $table_style, $installed_languages, $dflt_lang; + global $repository, $FA_repo_version; + + $th = array(_("Language"), _("Name"), _("Encoding"), _("Right To Left"), + _("Installed"), _("Available"), _("Default"), "", ""); + $currlang = $_SESSION["language"]->code; + + div_start('lang_tbl'); + start_form(); + // + // select/display system locales support for sites using native gettext + // + if (function_exists('gettext')) + { + if (check_value('DisplayAll')) + array_insert($th, 7, _("Supported")); + start_table(); + check_row(_('Display also languages not supported by server locales'), 'DisplayAll', null, true); + end_table(); + } + start_table(TABLESTYLE); + table_header($th); + + $k = 0; + + // get list of all (available and installed) langauges + $langs = get_languages_list(); + foreach ($langs as $pkg_name => $lng) + { + $lang = $lng['code']; + $lang_name = $lng['name']; + $charset = $lng['encoding']; + $rtl = @$lng['rtl'] == 'yes' || @$lng['rtl'] === true; + $available = @$lng['available']; + $installed = @$lng['version']; + $id = @$lng['local_id']; + + if ($lang == $currlang) + start_row("class='stockmankobg'"); + else + alt_table_row_color($k); + + $support = ($lang == 'en_GB') || + $_SESSION['get_text']->check_support($lang, $charset); + + if (function_exists('gettext') && !$support && !get_post('DisplayAll')) continue; + + label_cell($lang); + label_cell($available ? get_package_view_str($lang, $lang_name) : $lang_name); + label_cell($charset); + label_cell($rtl ? _("Yes") : _("No")); + + label_cell($id === null ? _("None") : + ($available && $installed ? $installed : _("Unknown"))); + + label_cell($available ? $available : _("None")); + + label_cell($id === null ? '' : + radio(null, 'CurDflt', $id, $dflt_lang == $lang, true), + "align='center'"); + + if (function_exists('gettext') && check_value('DisplayAll')) + label_cell($support ? _("Yes") :_("No")); + + if (!$available && ($lang != 'en_GB')) // manually installed language + button_cell('Edit'.$id, _("Edit"), _('Edit non standard language configuration'), + ICON_EDIT); + elseif (check_pkg_upgrade($installed, $available)) // outdated or not installed language in repo + button_cell('Update'.$pkg_name, $installed ? _("Update") : _("Install"), + _('Upload and install latest language package'), ICON_DOWN); + else + label_cell(''); + + if (($id !== null) && ($lang != $currlang) && ($lang != 'en_GB')) { + delete_button_cell('Delete'.$id, _('Delete')); + submit_js_confirm('Delete'.$id, + sprintf(_("You are about to remove language \'%s\'.\nDo you want to continue ?"), + $lang_name)); + } else + label_cell(''); + end_row(); + } + end_table(); + display_note(_("The marked language is the current language which cannot be deleted."), 0, 0, "class='currentfg'"); + br(); + submit_center_first('Refresh', _("Update default"), '', null); + + submit_center_last('Add', _("Add new language manually"), '', false); + + end_form(); + div_end(); +} +//--------------------------------------------------------------------------------------------- +// Non standard (manually entered) languages support. +// function check_data() { - if ($_POST['code'] == "" || $_POST['name'] == "" || $_POST['encoding'] == "") { + global $installed_languages; + + if (get_post('code') == '' || get_post('name') == '' || get_post('encoding') == '') { display_error(_("Language name, code nor encoding cannot be empty")); return false; } + $id = array_search_value($_POST['code'], $installed_languages, 'code'); + if ($id !== null && $installed_languages[$id]['package'] != null) { + display_error(_('Standard package for this language is already installed. If you want to install this language manually, uninstall standard language package first.')); + return false; + } return true; } -//--------------------------------------------------------------------------------------------- - -function handle_submit() +function handle_submit($id) { - global $path_to_root, $installed_languages, $dflt_lang; - - if (!check_data()) - return false; - - $id = $_GET['id']; + global $path_to_root, $installed_languages, $dflt_lang, $Mode; if ($_POST['dflt']) { $dflt_lang = $_POST['code']; @@ -61,8 +152,11 @@ function handle_submit() $installed_languages[$id]['code'] = $_POST['code']; $installed_languages[$id]['name'] = $_POST['name']; + $installed_languages[$id]['path'] = 'lang/' . $_POST['code']; $installed_languages[$id]['encoding'] = $_POST['encoding']; $installed_languages[$id]['rtl'] = (bool)$_POST['rtl']; + $installed_languages[$id]['package'] = ''; + $installed_languages[$id]['version'] = ''; if (!write_lang()) return false; $directory = $path_to_root . "/lang/" . $_POST['code']; @@ -92,123 +186,30 @@ function handle_submit() //--------------------------------------------------------------------------------------------- -function handle_delete() -{ - global $path_to_root, $installed_languages, $dflt_lang; - - $id = $_GET['id']; - - $lang = $installed_languages[$id]['code']; - $filename = "$path_to_root/lang/$lang/LC_MESSAGES"; - - if ($lang == $dflt_lang ) { - // on delete set default to current. - $dflt_lang = $_SESSION['language']->code; - } - - unset($installed_languages[$id]); - $installed_languages = array_values($installed_languages); - - if (!write_lang()) - return; - - $filename = "$path_to_root/lang/$lang"; - flush_dir($filename); - rmdir($filename); - - meta_forward($_SERVER['PHP_SELF']); -} - -//--------------------------------------------------------------------------------------------- - -function display_languages() -{ - global $installed_languages, $dflt_lang; - - $lang = $_SESSION["language"]->code; - - echo " - "; - start_table(TABLESTYLE); - $th = array(_("Language"), _("Name"), _("Encoding"), _("Right To Left"), _("Default"), "", ""); - table_header($th); - - $k = 0; - $conn = $installed_languages; - $n = count($conn); - for ($i = 0; $i < $n; $i++) - { - if ($conn[$i]['code'] == $lang) - start_row("class='stockmankobg'"); - else - alt_table_row_color($k); - - label_cell($conn[$i]['code']); - label_cell($conn[$i]['name']); - label_cell($conn[$i]['encoding']); - if (isset($conn[$i]['rtl']) && $conn[$i]['rtl']) - $rtl = _("Yes"); - else - $rtl = _("No"); - label_cell($rtl); - label_cell($dflt_lang == $conn[$i]['code'] ? _("Yes") :_("No")); - $edit = _("Edit"); - $delete = _("Delete"); - if (user_graphic_links()) - { - $edit = set_icon(ICON_EDIT, $edit); - $delete = set_icon(ICON_DELETE, $delete); - } - label_cell("$edit"); - label_cell($conn[$i]['code'] == $lang ? '' : - "$delete"); - end_row(); - } - - end_table(); - display_note(_("The marked language is the current language which cannot be deleted."), 0, 0, "class='currentfg'"); -} - -//--------------------------------------------------------------------------------------------- - function display_language_edit($selected_id) { global $installed_languages, $dflt_lang; - if ($selected_id != -1) - $n = $selected_id; - else + if ($selected_id == -1) $n = count($installed_languages); - + else + $n = $selected_id; + start_form(true); - echo " - "; - start_table(TABLESTYLE2); if ($selected_id != -1) { - $conn = $installed_languages[$selected_id]; - $_POST['code'] = $conn['code']; - $_POST['name'] = $conn['name']; - $_POST['encoding'] = $conn['encoding']; + $lang = $installed_languages[$n]; + $_POST['code'] = $lang['code']; + $_POST['name'] = $lang['name']; + $_POST['encoding'] = $lang['encoding']; if (isset($conn['rtl'])) - $_POST['rtl'] = $conn['rtl']; + $_POST['rtl'] = $lang['rtl']; else $_POST['rtl'] = false; - $_POST['dflt'] = $dflt_lang == $conn['code']; + $_POST['dflt'] = $dflt_lang == $lang['code']; hidden('selected_id', $selected_id); } text_row_ex(_("Language Code"), 'code', 20); @@ -223,41 +224,71 @@ function display_language_edit($selected_id) end_table(0); display_note(_("Select your language files from your local harddisk."), 0, 1); - echo "
"; + submit_add_or_update_center(false, '', 'both'); end_form(); } - -//--------------------------------------------------------------------------------------------- - -if (isset($_GET['c'])) +function handle_delete($id) { - if ($_GET['c'] == 'df') - { - handle_delete(); + global $path_to_root, $installed_languages, $dflt_lang; + + $lang = $installed_languages[$id]['code']; + if ($installed_languages[$id]['package']) + if (!uninstall_package($installed_languages[$id]['package'])) + return; + + if ($lang == $dflt_lang ) { + // on delete set default to current. + $dflt_lang = $_SESSION['language']->code; } + + unset($installed_languages[$id]); + $installed_languages = array_values($installed_languages); - if ($_GET['c'] == 'u') - { - if (handle_submit()) - { - //meta_forward($_SERVER['PHP_SELF']); - } + if (!write_lang()) + return; + + $dirname = "$path_to_root/lang/$lang"; + if ($lang && is_dir($dirname)) { // remove nonstadard language dir + flush_dir($dirname, true); + rmdir($dirname); } } //--------------------------------------------------------------------------------------------- -display_languages(); +if ($Mode == 'Delete') + handle_delete($selected_id); -hyperlink_no_params($_SERVER['PHP_SELF'], _("Create a new language")); +if ($Mode == 'ADD_ITEM' || $Mode == 'UPDATE_ITEM') + if (check_data() && handle_submit($selected_id)) + $Mode = 'RESET'; -display_language_edit($selected_id); +if ($id = find_submit('Update', false)) + install_language($id); +if (get_post('_CurDflt_update') || (get_post('Refresh') && get_post('CurDflt', -1) != -1)) { + $new_lang = $installed_languages[get_post('CurDflt', 0)]['code']; + if ($new_lang != $dflt_lang) { + $dflt_lang = $new_lang; + write_lang(); + $Ajax->activate('lang_tbl'); + } +} +if (get_post('_DisplayAll_update')) { + $Ajax->activate('lang_tbl'); +} + //--------------------------------------------------------------------------------------------- +if (isset($_GET['popup']) || get_post('Add') || $Mode == 'Edit' || $Mode == 'ADD_ITEM' || $Mode == 'UPDATE_ITEM') { + display_language_edit($selected_id); +} else + display_languages(); + +//--------------------------------------------------------------------------------------------- end_page(); ?> \ No newline at end of file diff --git a/admin/inst_module.php b/admin/inst_module.php index 844244b7..52190ca7 100644 --- a/admin/inst_module.php +++ b/admin/inst_module.php @@ -12,6 +12,7 @@ $page_security = 'SA_CREATEMODULES'; $path_to_root=".."; include_once($path_to_root . "/includes/session.inc"); +include_once($path_to_root."/includes/packages.inc"); page(_($help_context = "Install/Activate extensions")); @@ -21,38 +22,9 @@ include_once($path_to_root . "/admin/db/maintenance_db.inc"); include_once($path_to_root . "/includes/ui.inc"); simple_page_mode(true); - //--------------------------------------------------------------------------------------------- -function update_extensions($extensions) { - global $db_connections; - - if (!write_extensions($extensions)) { - display_notification(_("Cannot update system extensions list.")); - return false; - } - - // update per company files - $cnt = count($db_connections); - for($i = 0; $i < $cnt; $i++) - { - $newexts = $extensions; - // update 'active' status - $exts = get_company_extensions($i); - foreach ($exts as $key => $ext) - { - if (isset($newexts[$key])) - $newexts[$key]['active'] = $exts[$key]['active']; - } - if(!write_extensions($newexts, $i)) - { - display_notification(sprintf(_("Cannot update extensions list for company '%s'."), - $db_connections[$i]['name'])); - return false; - } - } - return true; -} - +// Check third-party extension parameters +// function check_data($id, $exts) { if ($_POST['name'] == "") { @@ -93,17 +65,21 @@ function handle_submit() $id = $selected_id==-1 ? $next_extension_id : $selected_id; if ($selected_id != -1 && $extensions[$id]['type'] != 'plugin') { - display_error(_('Module installation support is not implemented yet. You have to do it manually.')); + display_error(_('Module installation support is not implemented.')); return; } - $extensions[$id]['tab'] = $_POST['tab']; $extensions[$id]['name'] = $_POST['name']; - $extensions[$id]['path'] = $_POST['path']; - $extensions[$id]['title'] = $_POST['title']; + $extensions[$id]['package'] = ''; + $extensions[$id]['version'] = ''; $extensions[$id]['active'] = check_value('active'); + $entry = $selected_id == -1 ? array() : $extensions[$id]['entries'][0]; + + $entry['tab_id'] = $_POST['tab']; + $entry['title'] = $_POST['title']; + $entry['section'] = 2; // menu section aka module - // Currently we support only plugin extensions here. + // Only plugin extensions can be installed manually. $extensions[$id]['type'] = 'plugin'; $directory = $path_to_root . "/modules/" . $_POST['path']; if (!file_exists($directory)) @@ -112,7 +88,7 @@ function handle_submit() } if (is_uploaded_file($_FILES['uploadfile']['tmp_name'])) { - $extensions[$id]['filename'] = $_FILES['uploadfile']['name']; + $entry['url'] = '/modules/'.$_POST['path'].'/'.$_FILES['uploadfile']['name']; $file1 = $_FILES['uploadfile']['tmp_name']; $file2 = $directory . "/".$_FILES['uploadfile']['name']; if (file_exists($file2)) @@ -120,21 +96,18 @@ function handle_submit() move_uploaded_file($file1, $file2); } else - $extensions[$id]['filename'] = get_post('filename'); + $entry['url'] = '/modules/'.$_POST['path'].'/'.get_post('filename'); + if (is_uploaded_file($_FILES['uploadfile2']['tmp_name'])) { $file1 = $_FILES['uploadfile2']['tmp_name']; - $file2 = $directory . "/".$_FILES['uploadfile2']['name']; - if (file_exists($file2)) - unlink($file2); - move_uploaded_file($file1, $file2); $db_name = $_SESSION["wa_current_user"]->company; - db_import($file2, $db_connections[$db_name]); + db_import($file1, $db_connections[$db_name]); } if (is_uploaded_file($_FILES['uploadfile3']['tmp_name'])) { - $extensions[$id]['acc_file'] = $_FILES['uploadfile3']['name']; + $extensions[$id]['acc_file'] = '/modules/'.$_POST['path'].'/'.$_FILES['uploadfile3']['name']; $file1 = $_FILES['uploadfile3']['tmp_name']; $file2 = $directory . "/".$_FILES['uploadfile3']['name']; if (file_exists($file2)) @@ -142,19 +115,18 @@ function handle_submit() move_uploaded_file($file1, $file2); } else - $extensions[$id]['acc_file'] = get_post('acc_file'); + $extensions[$id]['acc_file'] = '/modules/'.$_POST['path'].'/'.get_post('acc_file'); // security area guess for plugins - if ($extensions[$id]['type'] == 'plugin'){ - $exttext = file_get_contents($path_to_root.'/modules/' - .$extensions[$id]['path'].'/'.$extensions[$id]['filename']); - $area = 'SA_OPEN'; - if (preg_match('/.*\$page_security\s*=\s*[\'"]([^\'"]*)/', $exttext, $match)) { - $area = trim($match[1]); - } - $extensions[$id]['access'] = $area; + $exttext = file_get_contents($path_to_root.$entry['url']); + $area = 'SA_OPEN'; + if (preg_match('/.*\$page_security\s*=\s*[\'"]([^\'"]*)/', $exttext, $match)) { + $area = trim($match[1]); } + $entry['access'] = $area; + $extensions[$id]['entries'] = array($entry); + if ($selected_id == -1) { $next_extension_id++; @@ -164,76 +136,116 @@ function handle_submit() return true; } -function handle_delete() +function handle_delete($id) { - global $path_to_root, $db_connections, $selected_id; + global $path_to_root; $extensions = get_company_extensions(); - $id = $selected_id; - - $filename = $path_to_root - . ($extensions[$id]['type']=='plugin' ? "/modules/": '/') - . $extensions[$id]['path']; + if ($extensions[$id]['package'] != '') { + if (!uninstall_package($extensions[$id]['package'])) + return false; + } else { - flush_dir($filename); - rmdir($filename); + $dirname = dirname(@$extensions[$id]['entries'][0]['url']); + if ($dirname) { + $dirname = $path_to_root.$dirname; + flush_dir($dirname, true); + rmdir($dirname); + } + } unset($extensions[$id]); - if (update_extensions($extensions)) + if (update_extensions($extensions)) { display_notification(_("Selected extension has been successfully deleted")); + } return true; } - +// +// Helper for formating menu tabs/entries to be displayed in extension table +// +function fmt_titles($defs) +{ + if (!$defs) return ''; + foreach($defs as $def) { + $str[] = access_string($def['title'], true); + } + return implode('
', array_values($str)); +} //--------------------------------------------------------------------------------------------- - +// +// Display list of all extensions - installed and available from repository +// function display_extensions() { + + div_start('ext_tbl'); start_table(TABLESTYLE); - $th = array(_("Name"),_("Tab"), _("Link text"), _("Folder"), _("Filename"), - _("Access extensions"),"", ""); + + $th = array(_("Extension"),_("Modules provided"), _("Options provided"), + _("Installed"), _("Available"), "", ""); table_header($th); $k = 0; - $mods = get_company_extensions(); - $mods = array_natsort($mods, null, 'name'); + $mods = get_extensions_list(); - foreach($mods as $i => $mod) + foreach($mods as $pkg_name => $ext) { - $is_mod = $mod['type'] == 'module'; - alt_table_row_color($k); - label_cell($mod['name']); - label_cell( $is_mod ? - $mod['title'] : access_string($_SESSION['App']->applications[$mod['tab']]->name, true)); - $ttl = access_string($mod['title']); - label_cell($ttl[0]); - label_cell($mod['path']); - label_cell($mod['filename']); - label_cell(@$mod['acc_file']); - if ($is_mod) - { - label_cell(''); // not implemented (yet) - } + $available = @$ext['available']; + $installed = @$ext['version']; + $id = @$ext['local_id']; + $is_mod = $ext['type'] == 'module'; + + $entries = fmt_titles(@$ext['entries']); + $tabs = fmt_titles(@$ext['tabs']); + + alt_table_row_color($k); +// label_cell(is_array($ext['Descr']) ? $ext['Descr'][0] : $ext['Descr']); + label_cell($available ? get_package_view_str($pkg_name, $ext['name']) : $ext['name']); + label_cell($tabs); + label_cell($entries); + + label_cell($id === null ? _("None") : + ($available && $installed ? $installed : _("Unknown"))); + label_cell($available ? $available : _("None")); + + if (!$available && $ext['type'] == 'plugin') // third-party plugin + button_cell('Edit'.$id, _("Edit"), _('Edit third-party extension parameters.'), + ICON_EDIT); + elseif (check_pkg_upgrade($installed, $available)) // outdated or not installed extension in repo + button_cell('Update'.$pkg_name, $installed ? _("Update") : _("Install"), + _('Upload and install latest extension package'), ICON_DOWN); else - { - edit_button_cell("Edit".$i, _("Edit")); - } - delete_button_cell("Delete".$i, _("Delete")); - submit_js_confirm('Delete'.$i, _('You are about to delete this extension\nDo you want to continue?')); + label_cell(''); + + if ($id !== null) { + delete_button_cell('Delete'.$id, _('Delete')); + submit_js_confirm('Delete'.$id, + sprintf(_("You are about to remove package \'%s\'.\nDo you want to continue ?"), + $ext['name'])); + } else + label_cell(''); + end_row(); } end_table(1); -} + submit_center_first('Refresh', _("Update"), '', null); + submit_center_last('Add', _("Add third-party plugin"), '', false); + + div_end(); +} +//--------------------------------------------------------------------------------- +// +// Get all installed extensions and display +// with current status stored in company directory. +// function company_extensions($id) { start_table(TABLESTYLE); - $th = array(_("Name"),_("Tab"), _("Link text"), _("Active")); + $th = array(_("Extension"),_("Modules provided"), _("Options provided"), _("Active")); - // get all available extensions and display - // with current status stored in company directory. - $mods = get_company_extensions(); $exts = get_company_extensions($id); foreach($mods as $key => $ins) { @@ -250,26 +262,28 @@ function company_extensions($id) { alt_table_row_color($k); label_cell($mod['name']); - label_cell( $mod['type'] == 'module' ? - $mod['title'] : access_string($_SESSION['App']->applications[$mod['tab']]->name, true)); - $ttl = access_string($mod['title']); - label_cell($ttl[0]); + $entries = fmt_titles(@$mod['entries']); + $tabs = fmt_titles(@$mod['tabs']); + label_cell($tabs); + label_cell($entries); + check_cells(null, 'Active'.$i, @$mod['active'] ? 1:0, false, false, "align='center'"); end_row(); } end_table(1); - submit_center('Update', _('Update'), true, false, 'default'); + submit_center('Refresh', _('Update'), true, false, 'default'); } //--------------------------------------------------------------------------------------------- - +// +// Third-party plugin installation +// function display_ext_edit($selected_id) { global $Mode; - $extensions = get_company_extensions(); start_table(TABLESTYLE2); @@ -278,12 +292,14 @@ function display_ext_edit($selected_id) { if ($Mode == 'Edit') { $mod = $extensions[$selected_id]; - $_POST['tab'] = $mod['tab']; + $entry = $mod['entries'][0]; + $_POST['name'] = $mod['name']; - $_POST['title'] = $mod['title']; - $_POST['path'] = $mod['path']; - $_POST['filename'] = $mod['filename']; - $_POST['acc_file'] = @$mod['acc_file']; + $_POST['tab'] = $entry['tab_id']; + $_POST['title'] = $entry['title']; + $_POST['path'] = substr(dirname($entry['url']), 9); //strip '/modules/' + $_POST['filename'] = basename($entry['url']); + $_POST['acc_file'] = @$mod['acc_file'] ? basename($mod['acc_file']) : null; hidden('filename', $_POST['filename']); hidden('acc_file', $_POST['acc_file']); } @@ -318,12 +334,18 @@ if ($Mode=='ADD_ITEM' || $Mode == 'UPDATE_ITEM') { } if ($Mode == 'Delete') { - handle_delete(); + handle_delete($selected_id); $Mode = 'RESET'; } -if (get_post('Update')) { - $exts = get_company_extensions(); +if (get_post('Refresh')) { + $exts = get_company_extensions(get_post('extset')); // + $comp = get_post('extset'); + foreach($exts as $i => $ext) { + if ($ext['package'] && ($ext['active'] ^ check_value('Active'.$i))) { + $pkg = new package($ext['package'].'-'.$ext['version'].'.pkg'); + $pkg->support(check_value('Active'.$i) ? 'activate':'deactivate', $comp); + } $exts[$i]['active'] = check_value('Active'.$i); } write_extensions($exts, get_post('extset')); @@ -332,6 +354,9 @@ if (get_post('Update')) { display_notification(_('Current active extensions set has been saved.')); } +if ($id = find_submit('Update', false)) + install_extension($id); + if ($Mode == 'RESET') { $selected_id = -1; @@ -343,21 +368,26 @@ start_form(true); if (list_updated('extset')) $Ajax->activate('_page_body'); -echo "
" . _('Extensions:') . "  "; -echo extset_list('extset', null, true); -echo "

"; - $set = get_post('extset', -1); -if ($set == -1) { - display_extensions(); +if (isset($_GET['popup']) || get_post('Add') || $Mode == 'Edit' + || $Mode == 'ADD_ITEM' || $Mode == 'UPDATE_ITEM') +{ display_ext_edit($selected_id); -} else { - company_extensions($set); } +else { + echo "
" . _('Extensions:') . "  "; + echo extset_list('extset', null, true); + echo "

"; + + if ($set == -1) + display_extensions(); + else + company_extensions($set); +} + //--------------------------------------------------------------------------------------------- end_form(); end_page(); - ?> \ No newline at end of file diff --git a/config.default.php b/config.default.php index 60205bec..34b86733 100644 --- a/config.default.php +++ b/config.default.php @@ -61,6 +61,16 @@ if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_ // src-data compatibility check. Do not change. $core_version = "2.3"; + // Extension packages repository + $FA_repo_version = '2.3'; // this should be get from database FA version? + $repo_auth = array( + 'login' => 'anonymous', + 'pass' => 'password', + ); + + $repository = 'http://'.$repo_auth['login'].':'.$repo_auth['pass'].'@'.'localhost/Repo' + .'/index.php?path='; + // Build for development purposes $build_version = date("d.m.Y", filemtime("$path_to_root/CHANGELOG.txt")); diff --git a/frontaccounting.php b/frontaccounting.php index 4ae88cbb..3324d6bb 100644 --- a/frontaccounting.php +++ b/frontaccounting.php @@ -20,14 +20,14 @@ if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_ include_once($path_to_root . '/applications/generalledger.php'); include_once($path_to_root . '/applications/setup.php'); include_once($path_to_root . '/installed_extensions.php'); - if (count($installed_extensions) > 0) + + foreach ($installed_extensions as $ext) { - foreach ($installed_extensions as $ext) - { - if ($ext['type'] == 'module') - include_once($path_to_root."/".$ext['path']."/".$ext['filename']); - } - } + if (@($ext['active'] && isset($ext['tabs']))) // supressed warnings before 2.2 upgrade + foreach($ext['tabs'] as $tab) { + include_once($path_to_root.'/'.$ext['path'].'/'.$tab['url']); + } + } class front_accounting { @@ -38,6 +38,7 @@ if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_ // GUI var $menu; //var $renderer; + function front_accounting() { //$this->renderer =& new renderer(); @@ -64,7 +65,9 @@ if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_ function display() { global $path_to_root; - include($path_to_root . "/themes/".user_theme()."/renderer.php"); + + include_once($path_to_root . "/themes/".user_theme()."/renderer.php"); + $this->init(); $rend = new renderer(); $rend->wa_header(); @@ -72,6 +75,7 @@ if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_ $rend->display_applications($this); //$rend->menu_footer($this->menu); $rend->wa_footer(); + $this->renderer =& $rend; } function init() { @@ -87,27 +91,25 @@ if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_ $this->add_application(new manufacturing_app()); $this->add_application(new dimensions_app()); $this->add_application(new general_ledger_app()); - if (count($installed_extensions) > 0) + + // Do not use global array directly here, or you suffer + // from buggy php behaviour (unexpected loop break + // because of same var usage in class constructor). + $extensions = $installed_extensions; + foreach ($extensions as $ext) { - // Do not use global array directly here, or you suffer - // from buggy php behaviour (unexpected loop break - // because of same var usage in class constructor). - $extensions = $installed_extensions; - foreach ($extensions as $ext) - { - if (@($ext['active'] && $ext['type'] == 'module')) // supressed warnings before 2.2 upgrade - { - $_SESSION['get_text']->add_domain($_SESSION['language']->code, - $ext['path']."/lang"); - $class = $ext['tab']."_app"; + if (@($ext['active'] && isset($ext['tabs']))) { // supressed warnings before 2.2 upgrade + $_SESSION['get_text']->add_domain($_SESSION['language']->code, + $ext['path']."/lang"); + foreach($ext['tabs'] as $tab) { + $class = $tab['tab_id']."_app"; if (class_exists($class)) $this->add_application(new $class()); - $_SESSION['get_text']->add_domain($_SESSION['language']->code, - $path_to_root."/lang"); } } - } - + } + $_SESSION['get_text']->add_domain($_SESSION['language']->code, + $path_to_root."/lang", $_SESSION['language']->version); $this->add_application(new setup_app()); } } diff --git a/includes/access_levels.inc b/includes/access_levels.inc index 77dcae9a..bb84a10a 100644 --- a/includes/access_levels.inc +++ b/includes/access_levels.inc @@ -300,7 +300,7 @@ function get_access_extensions($id) { $security_sections = $security_areas = array(); if (isset($ext['acc_file'])) - include($path_to_root.($ext['type'] == 'plugin' ? '/modules/':'/').$ext['path'].'/'.$ext['acc_file']); + include($path_to_root.'/'.$ext['path'].'/'.$ext['acc_file']); return array($security_areas, $security_sections); } diff --git a/includes/archive.inc b/includes/archive.inc new file mode 100644 index 00000000..d8316d39 --- /dev/null +++ b/includes/archive.inc @@ -0,0 +1,466 @@ +options = array ( + 'basedir' => ".", + 'name' => $name, + 'prepend' => "", + 'inmemory' => 0, + 'overwrite' => 0, + 'recurse' => 1, + 'storepaths' => 1, + 'followlinks' => 0, + 'level' => 3, + 'method' => 1, + 'sfx' => "", + 'type' => "", + 'comment' => "" + ); + $this->files = array (); + $this->exclude = array (); + $this->storeonly = array (); + $this->error = array (); + } + + function set_options($options) + { + foreach ($options as $key => $value) + $this->options[$key] = $value; + if (!empty ($this->options['basedir'])) + { + $this->options['basedir'] = str_replace("\\", "/", $this->options['basedir']); + $this->options['basedir'] = preg_replace("/\/+/", "/", $this->options['basedir']); + $this->options['basedir'] = preg_replace("/\/$/", "", $this->options['basedir']); + } + if (!empty ($this->options['name'])) + { + $this->options['name'] = str_replace("\\", "/", $this->options['name']); +// $this->options['name'] = preg_replace("/\/+/", "/", $this->options['name']); + } + if (!empty ($this->options['prepend'])) + { + $this->options['prepend'] = str_replace("\\", "/", $this->options['prepend']); + $this->options['prepend'] = preg_replace("/^(\.*\/+)+/", "", $this->options['prepend']); + $this->options['prepend'] = preg_replace("/\/+/", "/", $this->options['prepend']); + $this->options['prepend'] = preg_replace("/\/$/", "", $this->options['prepend']) . "/"; + } + } + + function create_archive() + { + $this->make_list(); + + if ($this->options['inmemory'] == 0) + { + if ($this->options['overwrite'] == 0 && + file_exists($this->options['name'] + . ($this->options['type'] == "pkg" || $this->options['type'] == "gzip" ||$this->options['type'] == "bzip" ? ".tmp" : ""))) + { + $this->error[] = "File {$this->options['name']} already exists."; + return 0; + } + else if (!($this->archive = @fopen($this->options['name'].".tmp", "wb+"))) + { + $this->error[] = "Could not open {$this->options['name']} for writing.".':'.getcwd(); + return 0; + } + } + else + $this->archive = ""; + + if (!$this->create_tar()) + { + $this->error[] = "Could not create package file."; + return 0; + } + if (!$this->create_pkg()) + { + $this->error[] = "Could not compress package file."; + return 0; + } + + if ($this->options['inmemory'] == 0) + { + fclose($this->archive); + unlink($this->options['name'] . ".tmp"); + } + } + + function add_data($data) + { + if ($this->options['inmemory'] == 0) + fwrite($this->archive, $data); + else + $this->archive .= $data; + } + + function make_list() + { + if (!empty ($this->exclude)) { + foreach ($this->files as $key => $value) + foreach ($this->exclude as $current) + if ($value['name'] == $current['name']) + unset ($this->files[$key]); + } + if (!empty ($this->storeonly)) + foreach ($this->files as $key => $value) + foreach ($this->storeonly as $current) + if ($value['name'] == $current['name']) + $this->files[$key]['method'] = 0; + unset ($this->exclude, $this->storeonly); + } + + function add_files($list) + { + $temp = $this->list_files($list); + foreach ($temp as $current) + $this->files[] = $current; + } + + function exclude_files($list) + { + $temp = $this->list_files($list); + foreach ($temp as $current) + $this->exclude[] = $current; + } + + function store_files($list) + { + $temp = $this->list_files($list); + foreach ($temp as $current) + $this->storeonly[] = $current; + } + + function list_files($list) + { + if (!is_array ($list)) + { + $temp = $list; + $list = array ($temp); + unset ($temp); + } + + $files = array (); + + $pwd = getcwd(); + chdir($this->options['basedir']); + + foreach ($list as $current) + { + $current = str_replace("\\", "/", $current); + $current = preg_replace("/\/+/", "/", $current); + $current = preg_replace("/\/$/", "", $current); + if (strstr($current, "*")) + { + $regex = preg_replace("/([\\\^\$\.\[\]\|\(\)\?\+\{\}\/])/", "\\\\\\1", $current); + $regex = str_replace("*", ".*", $regex); + $dir = strstr($current, "/") ? substr($current, 0, strrpos($current, "/")) : "."; + $temp = $this->parse_dir($dir); + foreach ($temp as $current2) + if (preg_match("/^{$regex}$/i", $current2['name'])) + $files[] = $current2; + unset ($regex, $dir, $temp, $current); + } + else if (@is_dir($current)) + { + $temp = $this->parse_dir($current); + foreach ($temp as $file) + $files[] = $file; + unset ($temp, $file); + } + else if (@file_exists($current)) + $files[] = array ('name' => $current, 'name2' => $this->options['prepend'] . + preg_replace("/(\.+\/+)+/", "", ($this->options['storepaths'] == 0 && strstr($current, "/")) ? + substr($current, strrpos($current, "/") + 1) : $current), + 'type' => @is_link($current) && $this->options['followlinks'] == 0 ? 2 : 0, + 'ext' => substr($current, strrpos($current, ".")), 'stat' => stat($current)); + } + + chdir($pwd); + + unset ($current, $pwd); + return $files; + } + + function parse_dir($dirname) + { + if ($this->options['storepaths'] == 1 && !preg_match("/^(\.+\/*)+$/", $dirname)) + $files = array (array ('name' => $dirname, 'name2' => $this->options['prepend'] . + preg_replace("/(\.+\/+)+/", "", ($this->options['storepaths'] == 0 && strstr($dirname, "/")) ? + substr($dirname, strrpos($dirname, "/") + 1) : $dirname), 'type' => 5, 'stat' => stat($dirname))); + else + $files = array (); + $dir = @opendir($dirname); + + while ($file = @readdir($dir)) + { + $fullname = $dirname . "/" . $file; + if ($file == "." || $file == "..") + continue; + else if (@is_dir($fullname)) + { + if (empty ($this->options['recurse'])) + continue; + $temp = $this->parse_dir($fullname); + foreach ($temp as $file2) + $files[] = $file2; + } + else if (@file_exists($fullname)) + $files[] = array ('name' => $fullname, 'name2' => $this->options['prepend'] . + preg_replace("/(\.+\/+)+/", "", ($this->options['storepaths'] == 0 && strstr($fullname, "/")) ? + substr($fullname, strrpos($fullname, "/") + 1) : $fullname), + 'type' => @is_link($fullname) && $this->options['followlinks'] == 0 ? 2 : 0, + 'ext' => substr($file, strrpos($file, ".")), 'stat' => stat($fullname)); + } + + @closedir($dir); + + return $files; + } +} + +class tar_file extends archive +{ + function tar_file($name) + { + $this->archive($name); + $this->options['type'] = "tar"; + } + + function create_tar() + { + $pwd = getcwd(); + chdir($this->options['basedir']); + $files = $this->files; + foreach ($files as $current) + { + if ($current['name'] == $this->options['name']) + continue; + if (strlen($current['name2']) > 99) + { + $path = substr($current['name2'], 0, strpos($current['name2'], "/", strlen($current['name2']) - 100) + 1); + $current['name2'] = substr($current['name2'], strlen($path)); + if (strlen($path) > 154 || strlen($current['name2']) > 99) + { + $this->error[] = "Could not add {$path}{$current['name2']} to archive because the filename is too long."; + continue; + } + } + $block = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12", $current['name2'], sprintf("%07o", + $current['stat'][2]), sprintf("%07o", $current['stat'][4]), sprintf("%07o", $current['stat'][5]), + sprintf("%011o", $current['type'] == 2 ? 0 : $current['stat'][7]), sprintf("%011o", $current['stat'][9]), + " ", $current['type'], $current['type'] == 2 ? @readlink($current['name']) : "", "ustar ", " ", + "Unknown", "Unknown", "", "", !empty ($path) ? $path : "", ""); + + $checksum = 0; + for ($i = 0; $i < 512; $i++) + $checksum += ord(substr($block, $i, 1)); + $checksum = pack("a8", sprintf("%07o", $checksum)); + $block = substr_replace($block, $checksum, 148, 8); + + if ($current['type'] == 2 || $current['stat'][7] == 0) + { $this->add_data($block); + } + else if ($fp = @fopen($current['name'], "rb")) + { + $this->add_data($block); + + while ($temp = fread($fp, 1048576)) { + $this->add_data($temp); + } + if ($current['stat'][7] % 512 > 0) + { + $temp = ""; + for ($i = 0; $i < 512 - $current['stat'][7] % 512; $i++) + $temp .= "\0"; + $this->add_data($temp); + } + fclose($fp); + } + else + $this->error[] = "Could not open file {$current['name']} for reading. It was not added."; + } + + $this->add_data(pack("a1024", "")); + + chdir($pwd); + + return 1; + } + /* + Extract files to base directory. + When $dry_run!=0 no file is written, + but list of existing files to be written is returned. + */ + function extract_files($dry_run = false) + { + $flist = array(); + if ($fp = $this->open_archive()) + { + $pwd = getcwd(); + // display_error($pwd.':'.$this->options['basedir']); + chdir($this->options['basedir']); + if ($this->options['inmemory'] == 1) + $this->files = array(); + + while ($block = fread($fp, 512)) + { + $temp = unpack("a100name/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1type/a100symlink/a6magic/a2temp/a32temp/a32temp/a8temp/a8temp/a155prefix/a12temp", $block); + $file = array ( + 'name' => $temp['prefix'] . $temp['name'], + 'stat' => array ( + 2 => octdec($temp['mode']), + 4 => octdec($temp['uid']), + 5 => octdec($temp['gid']), + 7 => octdec($temp['size']), + 9 => octdec($temp['mtime']), + ), + 'checksum' => octdec($temp['checksum']), + 'type' => $temp['type'], + 'magic' => $temp['magic'], + ); + if ($file['checksum'] == 0x00000000) + break; + else if (substr($file['magic'], 0, 5) != "ustar") + { + $this->error[] = "This script does not support extracting this type of tar file."; + break; + } + $block = substr_replace($block, " ", 148, 8); + $checksum = 0; + for ($i = 0; $i < 512; $i++) + $checksum += ord(substr($block, $i, 1)); + if ($file['checksum'] != $checksum) { + $this->error[] = "Could not extract from {$this->options['name']}, it is corrupt."; + break; + } + if ($dry_run || $this->options['inmemory'] == 1) + { + if ($file['type'] == 0) { + if($dry_run) { + $tst = check_write($file['name']); + if (!$tst) { + $this->error[] = "Could not open {$this->options['basedir']}/{$file['name']} for writing."; + break; + } else { + $flist[] = $file['name']; + if ( $tst < 0 && $this->options['overwrite'] == 0) + $this->error[] = "{$file['name']} already exists."; + } + fseek($fp, ($file['stat'][7] + 511) & ~511, SEEK_CUR); // skip data + } else { + $dat = fread($fp, $file['stat'][7]); + $tail = $file['stat'][7] % 512; + if ($tail) + fread($fp, 512 - $tail); + $file['data'] = $dat; + } + } + unset ($file['checksum'], $file['magic']); + $this->files[] = $file; + } + else if ($file['type'] == 5) + { + if (!is_dir($file['name'])) { + mkdir($file['name'], $file['stat'][2]); + } + } + else if ($this->options['overwrite'] == 0 && file_exists($file['name'])) + { + $this->error[] = "{$file['name']} already exists."; + continue; + } + else if ($file['type'] == 2) + { + symlink($temp['symlink'], $file['name']); + @chmod($file['name'], $file['stat'][2]); + } + else if ($new = @fopen($file['name'], "wb")) + { + fwrite($new, fread($fp, $file['stat'][7])); + fread($fp, (512 - $file['stat'][7] % 512) == 512 ? 0 : (512 - $file['stat'][7] % 512)); + fclose($new); + @chmod($file['name'], $file['stat'][2]); + } + else + { + $this->error[] = "Could not open {$file['name']} for writing."; + continue; + } +// @chown($file['name'], $file['stat'][4]); +// @chgrp($file['name'], $file['stat'][5]); + if (!$dry_run) { + @touch($file['name'], $file['stat'][9]); + } + unset ($file); + } + chdir($pwd); + } + else + $this->error[] = "Could not open file {$this->options['name']}"; + error_log('3'); + + return $flist; + } + + function open_archive() + { + return @fopen($this->options['name'], "rb"); + } +} + +class gzip_file extends tar_file +{ + function gzip_file($name) + { + $this->tar_file($name); + $this->options['type'] = "gzip"; + } + + function create_gzip() + { + if ($this->options['inmemory'] == 0) + { + if ($fp = gzopen($this->options['name'], "wb{$this->options['level']}")) + { + fseek($this->archive, 0); + while ($temp = fread($this->archive, 1048576)) + gzwrite($fp, $temp); + gzclose($fp); + } + else + { + $this->error[] = "Could not open {$this->options['name']} for writing."; + return 0; + } + } + else + $this->archive = gzencode($this->archive, $this->options['level']); + + return 1; + } + + function open_archive() + { + return @gzopen($this->options['name'], "rb"); + } +} + +?> \ No newline at end of file diff --git a/includes/packages.inc b/includes/packages.inc new file mode 100644 index 00000000..70b11c15 --- /dev/null +++ b/includes/packages.inc @@ -0,0 +1,647 @@ +. +***********************************************************************/ +include_once($path_to_root. "/includes/archive.inc"); + +define('PKG_CACHE_PATH', $path_to_root.'/modules/_cache'); +define('PUBKEY_PATH', $path_to_root); +define('REPO_URL', "$repository/$FA_repo_version"); +/* +$pkg_data_basedir = array( + 'language' => '/lang/', +// 'module' => '/', + 'extension' => '/modules/', +// 'plugin' => '/modules/', + 'theme' => '/themes/'); +*/ +// +// FrontAccounting package class +// +class package extends gzip_file { + function package($filename, $basedir=null) + { + global $path_to_root; + + if (!$basedir) { + $basedir = PKG_CACHE_PATH.'/'.substr(basename($filename), 0, -4); + if (file_exists($basedir)) { +// flush_dir($basedir, true); + } else + mkdir($basedir); + } + $this->archive($filename); + $this->set_options(array('basedir'=> $basedir)); + $this->options['type'] = "pkg"; + } + // + // Used by archive class. Use create_archive() instead. + // + function create_pkg() + { + return $this->create_gzip(); + } + // + // Install package and clean temp directory. + // + function install() + { + global $path_to_root; + + $success = true; + + $this->set_options(array('overwrite' => 1)); + $this->extract_files(); // extract package in cache directory + $cachepath = $this->options['basedir']; + $ctrl = get_control_file("$cachepath/_init/config"); + +// $targetdir = $pkg_data_basedir[$ctrl['Type']]; +// if ($targetdir) +// $targetdir = $path_to_root.$targetdir.$ctrl['Package']; +// else +// $targetdir = $path_to_root; + $targetdir = $path_to_root.'/'.$ctrl['InstallPath']; + + if (!is_dir($targetdir)) + mkdir($targetdir); + + $dpackage = new package("$cachepath/_data", $targetdir); + $dpackage->set_options(array('overwrite' => 1)); + + $flist = $dpackage->extract_files(true); + if (count($dpackage->error)) { + $this->error = array_merge($this->error, $dpackage->error); + return false; + } + copy_files($flist, $targetdir, "$cachepath/_back"); + + $dpackage->extract_files(); //install package in target directory + + $success &= $this->support('install'); + $success &= count($dpackage->error) == 0; + $this->error = array_merge($this->error, $dpackage->error); + return $success; + } + // + // Removing package related sources + // + function uninstall() + { + global $path_to_root; + + $success = true; + + $cachepath = $this->options['basedir']; + $ctrl = get_control_file("$cachepath/_init/config"); +// $targetdir = $pkg_data_basedir[$ctrl['Type']]; +// if ($targetdir) +// $targetdir = $path_to_root.$targetdir.$ctrl['Package']; +// else +// $targetdir = $path_to_root; + $targetdir = $path_to_root.'/'.$ctrl['InstallPath']; + + $dpackage = new package("$cachepath/_data", $targetdir); + + $flist = $dpackage->extract_files(true); + + $success &= copy_files($flist, "$cachepath/_back", $targetdir, true); + + $success &= $this->support('uninstall'); + + return $success; + } + // + // Purge all package related configuration and data. + // + function purge() + { + return true; + } + + // + // Call special function defined in package install class + // + function support($name, $params = null) + { + $cachepath = $this->options['basedir']; + if (file_exists("$cachepath/_init/init.php")) { + include("$cachepath/_init/init.php"); + if (method_exists($installer, $name)) { + set_include_path("$cachepath/_init".PATH_SEPARATOR.get_include_path()); +// $old = getcwd(); +// chdir("$cachepath/_init"); + $ret = $installer->$name($params); +// chdir($old); + return $ret; + } + } + return true; + } +} +// +// Changes field value read from control file (single, or multiline) into +// arrays of subfields if needed. +// +function ufmt_property($key, $value) +{ + // indexes used in output arrays + $sub_fields = array( + 'MenuTabs' => array('url', 'access', 'tab_id', 'title', 'section'), + 'MenuEntries' => array('url', 'access', 'tab_id', 'title'), + ); + if (!isset($sub_fields[$key])) + return $value==='' ? null : $value; + + $prop = array(); + + if (!is_array($value)) + $value = array($value); + foreach($value as $line) { + $indexes = $sub_fields[$key]; + $ret = array(); + preg_match_all('/(["])(?:\\\\?+.)*?\1|[^"\s][\S]*/', $line, $match); + foreach($match[0] as $n => $subf) { + if ($match[1][$n]) + $val = strtr(substr($subf, 1, -1), + array('\\"'=>'"')); + else + $val = $subf; + if (count($indexes)) + $ret[array_shift($indexes)] = $val; + else + $ret[] = $val; + } + if (count($ret)) + $prop[] = $ret; + } + return $prop; +} +//============================================================================= +// +// Retrieve control file and return as associative array +// $index is name of field used as key in result array, or null for numeric keys +// +function get_control_file($file, $index = false) { + + $list = gzopen($file, 'rb'); + if (!$list) return null; + + $repo = $pkg = array(); + $key = false; $value = ''; + $line = ''; + do { + $line = rtrim($line); + if (@ctype_space($line[0])) { // continuation of multiline property + if (strlen(ltrim($line))) { + if ($value !== '' && !is_array($value)) + $value = array($value); + $value[] = ltrim($line); + continue; + } + } + if ($key) { // save previous property if any + $pkg[$key] = ufmt_property($key, $value); + } + if (!strlen($line)) { // end of section + if (count($pkg)) { + if ($index !== true) { + if ($index === false) break; + if (!isset($pkg[$index])) { + display_error(_("No key field '$index' in file '$file'")); + return null; + } + $repo[$pkg[$index]] = $pkg; + } else + $repo[] = $pkg; + } + $pkg = array(); + $key = null; $value = ''; + continue; +// } elseif (preg_match('/([\w-]*):\s*(.*)/', $line, $m)) { + } elseif (preg_match('/([^:]*):\s*(.*)/', $line, $m)) { + $key = $m[1]; $value = $m[2]; + if (!strlen($key)) { + display_error("Empty key in line $line"); + return null; + } + } else { + display_error("File parse error in line $line"); + return null; + } + + } while ((($line = fgets($list))!==false) || $key); + fclose($list); + + return $index === false ? $pkg : $repo; +} +// +// Save configuration data to control file. +// +function save_control_file($fname, $list, $zip=false) +{ + $file = $zip ? gzopen($fname, 'wb') : fopen($fname, 'wb'); + foreach($list as $section) { + foreach($section as $key => $value) { + if (is_array($value)) { // multiline value + if (is_array(reset($value))) { // lines have subfields + foreach($value as $i => $line) { + // Subfields containing white spaces or double quotes are doublequoted + // with " escaped with backslash. + foreach($line as $n => $subfield) + if (preg_match('/[\s"]/', $subfield)) { + $value[$i][$n] = + '"'.strtr($subfield, array('"'=>'\\"')).'"'; + } + // Subfields are separated by white space. + $value[$i] = implode(' ', $value[$i]); + } + } + // array elements on subsequent lines starting with white space + $value = //(count($value) ? "\n " :''). + implode("\n ", $value); + } + $zip ? gzwrite($file, "$key: $value\n") : fwrite($file, "$key: $value\n"); + } + $zip ? gzwrite($file, "\n"): fwrite($file, "\n"); + } + $zip ? gzclose($file) : fclose($file); +} +// +// Retrieve text field in localized version or default one +// when the localized is not avaialable. +// +function pkg_prop($pkg, $property, $lang=false) +{ + + if ($lang && isset($pkg[$property.'-'.user_language()])) + $prop = @$pkg[$pname]; + else + $prop = @$pkg[$property]; + + return is_array($prop) ? implode("\n ",$prop): $prop; +} +// +// Retrieve list of packages from repository and return as table ($pkgname==null), +// or find $pkgname package in repository and optionaly download +// +// $type is type/s of package +// $filter is optional field selection array in form field=>newkey +// or (0=>field1, 1=>field2...) +// $outkey - field used as a key in package list. If null 'Package' field is used. +// +function get_pkg_or_list($type = null, $pkgname = null, $filter=array(), $outkey=null, $download=true) { + + global $path_to_root, $repository, $FA_repo_version; + + // first download local copy of repo release file + // and check remote signature with local copy of public key + // + $loclist = PKG_CACHE_PATH.'/Release.gz'; + + if ($type!=null && !is_array($type)) { + $type = array($type); + } + $refresh = true; + do{ + if (!file_exists($loclist)) { + copy(REPO_URL.'/Release.gz', $loclist); + $refresh = false; + } + $sig = file_get_contents(REPO_URL.'/Release.sig', 'rb'); + $data = file_get_contents($loclist); + $cert = file_get_contents(PUBKEY_PATH.'/FA.pem'); + if (!openssl_verify($data, $sig, $cert)) { + if ($refresh) + @unlink($loclist); + else { + display_error(_('Release file in repository is invalid, or public key is outdated.')); + return null; + } + } else + $refresh = false; + } while($refresh); + + $Release = get_control_file($loclist, 'Filename'); + + // download and check all indexes containing given package types + // then complete package list or seek for pkg + $Packages = array(); + foreach($Release as $fname => $parms) { + if ($type && !count(array_intersect(explode(' ', $parms['Type']), $type))) { + unset($Release[$fname]); continue; // no packages of selected type in this index + } + if ($Release[$fname]['Version'] != $FA_repo_version) { + display_warning(_('Repository version does not match application version.')); // ? + } + $remoteindex = REPO_URL.'/'.$fname; + $locindex = PKG_CACHE_PATH.'/'.$fname; + $refresh = true; + do{ + if (!file_exists($locindex)) { + copy($remoteindex, $locindex); + $refresh = false; + } + if ($parms['SHA1sum'] != sha1_file($locindex)) { // check subdir index consistency + if ($refresh) + @unlink($locindex); + else { + display_error(sprintf( _("Security alert: broken index file in repository '%s'. Please inform repository administrator about this issue."), + $fname)); + return null; + } + } else + $refresh = false; + } while($refresh); + + // scan subdir list and select packages of given type + $pkglist = get_control_file($locindex, 'Package'); + foreach($pkglist as $name => $pkg) { + $pkgfullname = REPO_URL.'/'.$parms['Path']."/".$pkg['Filename'].'.pkg'; + if ($type==null || in_array($pkg['Type'], $type)) { + if (empty($filter)) + $p = $pkg; + else { + foreach($filter as $field => $key) { + if (is_numeric($field)) + $p[$field] = @$pkg[$field]; + else + $p[$key] = @$pkg[$field]; + } + } + if ($pkgname == null) { + $Packages[$outkey ? $outkey : $name] = $p; + } elseif ($pkgname == $pkg['Package']) { + //download package to temp directory + if ($download) { + $locname = "$path_to_root/tmp/".$pkg['Filename'].'.pkg'; + copy($pkgfullname, $locname); + // checking sha1 hash is expensive proces, so chekc the package + // consistency just before downloading + if ($pkg['SHA1sum'] != sha1_file($locname)) { + display_error(sprintf( _("Security alert: broken package '%s' in repository. Please inform repository administrator about this issue."), + $pkgfullname)); + return null; + } + } + return $p; + } + } + } + } + + return $Packages; +} + +function get_package($pkgname, $type = null) +{ + $all = get_pkg_or_list($type, $pkgname); + $pkg = array_search_value($all, $pkgname, 'Package'); +} +/* + Returns full name of installed package, or null if package is not installed. +*/ +function installed_package($package) +{ + $cache = opendir(PKG_CACHE_PATH); + + while ($file = @readdir($cache)) { + if (!is_dir(PKG_CACHE_PATH.'/'.$file)) + continue; + if (strpos($file, $package.'-') === 0) + return $file; + } + @closedir($cache); + + return null; +} +/* + Remove package from system +*/ +function uninstall_package($name) +{ + $name = installed_package($name); + if (!$name) return true; // not installed + $pkg = new package($name.'.pkg'); + $pkg->uninstall(); + if($name) { + flush_dir(PKG_CACHE_PATH.'/'.$name, true); + rmdir(PKG_CACHE_PATH.'/'.$name); + } + return count($pkg->error)==0; +} + +//--------------------------------------------------------------------------------------- +// +// Return merged list of available and installed languages in inform of local +// configuration array supplemented with installed versions information. +// +function get_languages_list() +{ + global $installed_languages; + + $pkgs = get_pkg_or_list('language', null, array( + 'Package' => 'package', + 'Version' => 'available', + 'Name' => 'name', + 'Language' => 'code', + 'Encoding' => 'encoding', + 'RTLDir' => 'rtl', + 'Description' => 'Descr', + 'InstallPath' => 'path' + )); + + // add/update languages already installed + // + foreach($installed_languages as $id => $l) { + $list = array_search_keys($l['code'], $pkgs, 'code'); // get all packages with this code + foreach ($list as $name) { + if ($l['encoding'] == $pkgs[$name]['encoding']) { // if the same encoding + $pkgs[$name]['version'] = $l['version']; // set installed version + $pkgs[$name]['local_id'] = $id; // index in installed_languages + continue 2; + } + } + $l['local_id'] = $id; + if (!isset($pkgs[$l['package']]) || $l['package'] == '') + $pkgs[] = $l; + else + $pkgs[$l['package']] = array_merge($pkgs[$l['package']], $l); + } + ksort($pkgs); + return $pkgs; +} +//--------------------------------------------------------------------------------------- +// +// Return merged list of available and installed extensions in inform of local +// configuration array supplemented with installed versions information. +// +function get_extensions_list() +{ + $pkgs = get_pkg_or_list('extension', null, array( + 'Package' => 'package', + 'Version' => 'available', + 'Name' => 'name', + 'Description' => 'Descr', + 'Type' => 'type', + 'DefaultStatus'=> 'active', + 'MenuTabs' => 'tabs', + 'MenuEntries' => 'entries', + 'AccessExtensions' => 'acc_file', + 'InstallPath' => 'path' + )); + + // add/update extensions already installed + // + $local = get_company_extensions(); + foreach($local as $extno => $ext) { + if ($ext['type'] == 'theme') continue; + $ext['local_id'] = $extno; + if (!isset($pkgs[$ext['package']]) || $ext['package'] == '') + $pkgs[] = $ext; + else + $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext); + } + ksort($pkgs); + return $pkgs; +} +// +// Return merged list of available and installed extensions in inform of local +// configuration array supplemented with installed versions information. +// +function get_themes_list() +{ + $pkgs = get_pkg_or_list('theme', null, array( + 'Package' => 'package', + 'Version' => 'available', + 'Name' => 'name', + 'Description' => 'Descr' + )); + + // add/update extensions already installed + // + $local = get_company_extensions(); + + foreach($local as $extno => $ext) { + if (isset($pkgs[$ext['package']])) { + $ext['local_id'] = $extno; + $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext); + } + } + // TODO: Add other themes from themes directory + + ksort($pkgs); + return $pkgs; +} +//--------------------------------------------------------------------------------------------- +// Install/update package from repository +// +function install_language($pkg_name) +{ + global $path_to_root, $installed_languages, $Ajax; + + $pkg = get_pkg_or_list('language', $pkg_name); + + if ($pkg) { + $i = array_search_key($pkg['Language'], $installed_languages, 'code'); + if ($i === null) + $i = count($installed_languages); + else { // remove another already installed package for this language + $old_pkg = $installed_languages[$i]['package']; + if ($old_pkg && ($pkg['Package'] != $old_pkg)) + uninstall_package($old_pkg); + } + + $package = new package("$path_to_root/tmp/".$pkg['Filename'].'.pkg'); + if ($package->install()) { + $lang = array( + 'name' => $pkg['Name'], + 'package' => $pkg['Package'], + 'code' => $pkg['Language'], + 'encoding' => $pkg['Encoding'], + 'version' => $pkg['Version'], + 'path' => $pkg['InstallPath'] + ); + if ($pkg['RTLDir']=='yes') + $lang['rtl'] = true; + $installed_languages[$i] = $lang; + write_lang($installed_languages); + unlink("$path_to_root/tmp/".$pkg['Filename'].'.pkg'); + $Ajax->activate('lang_tbl'); + } + + } + +} +//--------------------------------------------------------------------------------------------- +// Install/update extension or theme package from repository +// +function install_extension($pkg_name) +{ + global $path_to_root, $next_extension_id, $Ajax; + + $pkg = get_pkg_or_list(array('extension', 'theme'), $pkg_name); + if ($pkg) { + $package = new package("$path_to_root/tmp/".$pkg['Filename'].'.pkg'); + $local_exts = get_company_extensions(); + if ($package->install()) { + $ext_id = array_search_key($pkg['Package'], $local_exts, 'package'); + if ($ext_id === null) + $ext_id = $next_extension_id++; + $ext = array( + 'name' => $pkg['Name'], + 'package' => $pkg['Package'], + 'version' => $pkg['Version'], + 'type' => $pkg['Type'], + 'active' => true, + 'path' => $pkg['InstallPath'], +// 'tabs' => $pkg['MenuTabs'], +// 'entries' => $pkg['MenuEntries'], +// 'acc_file' => @$pkg['AccessExtensions'], + ); + if (isset($pkg['MenuTabs'])) + $ext['tabs'] = $pkg['MenuTabs']; + if (isset($pkg['MenuEntries'])) + $ext['entries'] = $pkg['MenuEntries']; + if (isset($pkg['AccessExtensions'])) + $ext['acc_file'] = $pkg['AccessExtensions']; + $local_exts[$ext_id] = $ext; + update_extensions($local_exts); + unlink("$path_to_root/tmp/".$pkg['Filename'].'.pkg'); + $Ajax->activate('ext_tbl'); + } else { + display_error(implode('
', $package->error)); + } + } +} +/* + Returns true if newer package version is available +*/ +function check_pkg_upgrade($current, $available) +{ + preg_match_all('/[\d]+/', $available, $aver); + if (!count($aver[0])) + return false; + preg_match_all('/[\d]+/', $current, $cver); + if (!count($cver[0])) + return true; + foreach($aver[0] as $n => $ver) + if ($ver>@$cver[0][$n]) + return true; + return false; +} + +// +// Returns package info from index file +// +function get_package_info($pkg, $type=null, $filter=array(), $outkey=null, $download=true) { + return get_pkg_or_list($type, $pkg, $filter, null, false); +} + +?> \ No newline at end of file diff --git a/includes/ui/ui_controls.inc b/includes/ui/ui_controls.inc index 54315e2c..43b7fd81 100644 --- a/includes/ui/ui_controls.inc +++ b/includes/ui/ui_controls.inc @@ -217,7 +217,6 @@ function viewer_link($label, $url='', $class='', $id='', $icon=null) } else $preview_str = $label; - return $preview_str; } diff --git a/includes/ui/ui_view.inc b/includes/ui/ui_view.inc index cce5b51d..69a4e556 100644 --- a/includes/ui/ui_view.inc +++ b/includes/ui/ui_view.inc @@ -201,6 +201,19 @@ function get_journal_trans_view_str($type, $trans_no, $label="", $icon=false, return viewer_link($label, $viewer, $class, $id, $icon); } +//-------------------------------------------------------------------------------------- + +function get_package_view_str($pkg, $label="", $icon=false, $class='', $id='') +{ + if ($label == "") + { + $label = _("Info"); +// $icon = ICON_GL; + } + return viewer_link($label, "includes/ui/view_package.php?id=$pkg", $class, $id, $icon); +} + + //-------------------------------------------------------------------------------------- function get_trans_view_str($type, $trans_no, $label="", $icon=false, diff --git a/includes/ui/view_package.php b/includes/ui/view_package.php new file mode 100644 index 00000000..9c8022cd --- /dev/null +++ b/includes/ui/view_package.php @@ -0,0 +1,50 @@ + _('Available version'), + 'Type' => _('Package type'), + 'Name' => _('Package content'), + 'Description' => _('Description'), + 'Author' => _('Author'), + 'Homepage' => _('Home page'), + 'Maintenance' => _('Package maintainer'), + 'InstallPath' => _('Installation path'), + 'Depends' => _('Minimal software versions'), + 'RTLDir' => _('Right to left'), + 'Encoding' => _('Charset encoding') +); + +$pkg = get_package_info($_GET['id'], null, $filter); + +display_heading(sprintf(_("Content information for package '%s'"), $_GET['id'])); +br(); +start_table(TABLESTYLE2, "width=80%"); +$th = array(_("Property"), _("Value")); +table_header($th); + +foreach ($pkg as $field => $value) { + if ($value == '') + continue; + start_row(); + label_cells($field, htmlentities(is_array($value) ? implode('
', $value) :$value), + "class='tableheader2'"); + end_row(); +} +end_table(); + +end_page(); diff --git a/modules/_cache/index.php b/modules/_cache/index.php new file mode 100644 index 00000000..763940a7 --- /dev/null +++ b/modules/_cache/index.php @@ -0,0 +1,3 @@ + -- 2.30.2