feat: add type-aware server content workflows and workshop metadata support

Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/211fb593-b55a-42ad-b657-a3a4ca4764ff

Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-05-19 19:13:13 +00:00 committed by GitHub
parent caa80f6c3b
commit 7e183d77a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 676 additions and 71 deletions

View file

@ -1,5 +1,42 @@
$(function() {
// Tooltips are annoying
// Use title attribute instead
// $( 'input,textarea' ).tooltip();
var methodToRows = {
download_zip: ['#scm-row-url', '#scm-row-path', '#scm-row-post-script'],
download_file: ['#scm-row-url', '#scm-row-path', '#scm-row-post-script'],
steam_workshop: ['#scm-row-workshop-id', '#scm-row-workshop-app-id', '#scm-row-target-path-template', '#scm-row-optional-folder-name', '#scm-row-launch-param-additions', '#scm-row-config-edit-rule', '#scm-row-post-script'],
post_script: ['#scm-row-post-script'],
config_edit: ['#scm-row-path', '#scm-row-config-edit-rule', '#scm-row-post-script'],
create_folder: ['#scm-row-path']
};
var allRows = [
'#scm-row-url',
'#scm-row-path',
'#scm-row-workshop-id',
'#scm-row-workshop-app-id',
'#scm-row-target-path-template',
'#scm-row-optional-folder-name',
'#scm-row-post-script',
'#scm-row-config-edit-rule',
'#scm-row-launch-param-additions'
];
var $method = $('#scm-install-method');
var $help = $('#scm-install-method-help');
function applyContentTypeUi() {
if ($method.length === 0) return;
var value = $method.val();
var shown = methodToRows[value] || [];
for (var i = 0; i < allRows.length; i++) {
$(allRows[i]).hide();
}
for (var j = 0; j < shown.length; j++) {
$(shown[j]).show();
}
var selectedOption = $method.find('option:selected');
var helpText = selectedOption.data('help') || '';
$help.text(helpText);
$('#scm-path-label').text(value === 'config_edit' ? 'Config Target Path' : 'Target Path');
}
$method.on('change', applyContentTypeUi);
applyContentTypeUi();
});

View file

@ -46,6 +46,7 @@ define('LANG_plugin', "Plugins / Mods");
define('LANG_mappack', "Map Packs");
define('LANG_config', "Config Packs");
// Additional category labels (for future content types already defined in server_content_categories.php)
if (!defined('LANG_version')) define('LANG_version', "Version");
define('LANG_server_content_version', "Server Versions");
define('LANG_modpack', "Modpacks");
define('LANG_workshop', "Workshop Content");
@ -63,9 +64,15 @@ define('LANG_addons_db', "Server Content Database");
define('LANG_addon_has_been_created', "The server content item \"%s\" has been created.");
define('LANG_remove_addon', "Remove");
define('LANG_fill_the_url_address_to_a_compressed_file', "Please enter a URL for the compressed file to download.");
define('LANG_fill_the_download_url', "Please enter a download URL.");
define('LANG_fill_the_workshop_id', "Please enter a Workshop ID.");
define('LANG_fill_the_target_install_path', "Please select a target install path.");
define('LANG_fill_the_script_action_body', "Please enter a script/action body.");
define('LANG_fill_the_config_edit_rule', "Please enter a config edit rule.");
define('LANG_fill_the_addon_name', "Please enter a name for the server content item.");
define('LANG_select_an_addon_type', "Please select a content type.");
define('LANG_select_a_game_type', "Please select a game type.");
define('LANG_select_a_content_type', "Please select a content type.");
define('LANG_edit_addon', "Edit");
define('LANG_invalid_addon', "Invalid server content item or access denied.");
define('LANG_invalid_addon_type', "Invalid content type selected.");
@ -79,4 +86,16 @@ define('LANG_show_to_group', "Show to group");
define('LANG_all_groups', "All groups");
define('LANG_show_addons_for_selected_group', "Show content for selected group");
define('LANG_group', "Group");
define('LANG_content_type', "Content Type");
define('LANG_workshop_id', "Workshop ID");
define('LANG_target_path_template', "Target Path");
define('LANG_optional_folder_name', "Optional Folder Name");
define('LANG_config_edit_rule', "Config Edit Rule");
define('LANG_launch_param_additions', "Launch Parameter Additions");
define('LANG_content_type_help_download_zip', "Downloads and extracts an archive into the target path.");
define('LANG_content_type_help_download_file', "Downloads a single file without extraction.");
define('LANG_content_type_help_steam_workshop', "Downloads/updates a Workshop item and applies it to the server.");
define('LANG_content_type_help_post_script', "Runs a script/action only, with no URL required.");
define('LANG_content_type_help_config_edit', "Applies config edit rules to a target path, with no URL required.");
define('LANG_content_type_help_create_folder', "Creates target folders/paths only, with no URL required.");
?>

View file

@ -112,6 +112,10 @@ require_once("protocol/lgsl/lgsl_protocol.php");
// Central category map — all valid addon_type values and their labels.
require_once(dirname(__FILE__) . '/server_content_categories.php');
require_once(dirname(__FILE__) . '/server_content_helpers.php');
require_once(dirname(__FILE__) . '/workshop_action.php');
if (file_exists(dirname(__FILE__) . '/../steam_workshop/includes/functions.php')) {
require_once(dirname(__FILE__) . '/../steam_workshop/includes/functions.php');
}
function exec_ogp_module() {
@ -161,7 +165,7 @@ function exec_ogp_module() {
{
$addon_id = (int)$_REQUEST['addon_id'];
$addons_rows = $db->resultQuery("SELECT url, path, post_script, addon_type, install_method, content_version, requires_stop, restart_after_install FROM OGP_DB_PREFIXaddons WHERE addon_id=".$addon_id.$query_groups);
$addons_rows = $db->resultQuery("SELECT url, path, post_script, addon_type, install_method, content_version, requires_stop, restart_after_install, workshop_item_id, workshop_app_id, target_path_template, optional_folder_name, config_edit_rule, launch_param_additions, name FROM OGP_DB_PREFIXaddons WHERE addon_id=".$addon_id.$query_groups);
if (!is_array($addons_rows)) {
$addons_rows = [];
}
@ -174,10 +178,24 @@ function exec_ogp_module() {
$remote = new OGPRemoteLibrary($home_info['agent_ip'],$home_info['agent_port'],$home_info['encryption_key'],$home_info['timeout']);
$addon_info = $addons_rows[0];
$install_method = isset($addon_info['install_method']) ? $addon_info['install_method'] : 'download_zip';
$addon_info = $addons_rows[0];
$install_method = scm_get_install_method_default(isset($addon_info['install_method']) ? $addon_info['install_method'] : 'download_zip');
$content_version = isset($addon_info['content_version']) ? $addon_info['content_version'] : '';
$requires_stop = !empty($addon_info['requires_stop']) ? 1 : 0;
$post_script = '';
$validation_payload = array(
'url' => isset($addon_info['url']) ? $addon_info['url'] : '',
'path' => isset($addon_info['path']) ? $addon_info['path'] : '',
'workshop_item_id' => isset($addon_info['workshop_item_id']) ? $addon_info['workshop_item_id'] : '',
'target_path_template' => isset($addon_info['target_path_template']) ? $addon_info['target_path_template'] : '',
'post_script' => isset($addon_info['post_script']) ? $addon_info['post_script'] : '',
'config_edit_rule' => isset($addon_info['config_edit_rule']) ? $addon_info['config_edit_rule'] : '',
);
$validation_message = '';
if ($state == "start" && !scm_validate_install_method_payload($install_method, $validation_payload, $validation_message)) {
print_failure($validation_message);
return;
}
// ── requires_stop guard ───────────────────────────────────────────────
// If the content item requires the server to be stopped first, check
@ -261,7 +279,155 @@ function exec_ogp_module() {
$cache_mode
);
$_SESSION['scm_history_id_' . $home_id . '_' . $addon_id] = $history_id;
$pid = $remote->start_file_download( $addon_info['url'], $home_info['home_path']."/".$addon_info['path'], $filename, "uncompress", $post_script);
scm_log_content_install_action(array(
'addon_id' => (int)$addon_id,
'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '',
'content_type' => $install_method,
'home_id' => (int)$home_id,
'home_cfg_id' => (int)$home_info['home_cfg_id'],
'workshop_id' => isset($addon_info['workshop_item_id']) ? (string)$addon_info['workshop_item_id'] : '',
'target_path' => isset($addon_info['target_path_template']) ? (string)$addon_info['target_path_template'] : (string)$addon_info['path'],
'action' => 'started',
));
if ($install_method === 'steam_workshop') {
scm_ensure_workshop_schema($db);
$workshop_item_id = trim((string)$addon_info['workshop_item_id']);
$target_path_template = trim((string)$addon_info['target_path_template']);
$resolved = function_exists('steam_workshop_install_item_to_home')
? steam_workshop_install_item_to_home($db, $home_info, $workshop_item_id, $target_path_template, array(
'optional_folder_name' => trim((string)$addon_info['optional_folder_name']),
'workshop_app_id' => trim((string)$addon_info['workshop_app_id']),
))
: array('ok' => false);
if (empty($resolved['ok'])) {
$fallback_profile = function_exists('sw_get_profile_for_home') ? sw_get_profile_for_home($db, (int)$home_id) : false;
$fallback_workshop_app_id = trim((string)$addon_info['workshop_app_id']);
if ($fallback_workshop_app_id === '' && is_array($fallback_profile) && !empty($fallback_profile['workshop_app_id'])) {
$fallback_workshop_app_id = (string)$fallback_profile['workshop_app_id'];
}
if ($fallback_workshop_app_id === '') {
$fallback_workshop_app_id = scm_extract_workshop_app_id($server_xml);
}
$placeholder_map = scm_build_placeholder_map($home_info, array('exe_location' => (string)$server_xml->exe_location), array(
'WORKSHOP_ID' => $workshop_item_id,
'WORKSHOP_APP_ID' => $fallback_workshop_app_id,
'STEAM_APP_ID' => (is_array($fallback_profile) && !empty($fallback_profile['steam_app_id'])) ? (string)$fallback_profile['steam_app_id'] : '',
));
$resolved = array(
'workshop_app_id' => $fallback_workshop_app_id,
'steam_app_id' => (is_array($fallback_profile) && !empty($fallback_profile['steam_app_id'])) ? (string)$fallback_profile['steam_app_id'] : '',
'target_path_resolved' => scm_apply_placeholders($target_path_template, $placeholder_map),
);
}
$workshop_app_id = isset($resolved['workshop_app_id']) ? (string)$resolved['workshop_app_id'] : '';
$steam_app_id = isset($resolved['steam_app_id']) ? (string)$resolved['steam_app_id'] : '';
$target_path_resolved = isset($resolved['target_path_resolved']) ? (string)$resolved['target_path_resolved'] : '';
$extra_manifest = array(
'addon_id' => (int)$addon_id,
'target_path_template' => $target_path_template,
'target_path_resolved' => $target_path_resolved,
'optional_folder_name' => trim((string)$addon_info['optional_folder_name']),
'config_edit_rule' => trim((string)$addon_info['config_edit_rule']),
'launch_param_additions' => trim((string)$addon_info['launch_param_additions']),
'workshop_app_id' => $workshop_app_id,
'steam_app_id' => $steam_app_id,
);
$workshop_error = '';
$workshop_ok = scm_workshop_write_manifest_and_run($db, $home_info, $server_xml, 'install', array($workshop_item_id), $workshop_error, $extra_manifest);
if ($workshop_ok) {
scm_record_install_done($db, (int)$history_id, 'installed', 0, 'workshop_install_ok');
scm_upsert_manifest($db, $home_id, $addon_id, array(
'install_method' => $install_method,
'content_version' => $content_version,
'install_state' => 'installed',
'source_url' => 'steam://workshop/' . $workshop_item_id,
'installed_by' => $user_id,
));
scm_log_content_install_action(array(
'addon_id' => (int)$addon_id,
'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '',
'content_type' => $install_method,
'home_id' => (int)$home_id,
'home_cfg_id' => (int)$home_info['home_cfg_id'],
'workshop_id' => $workshop_item_id,
'target_path' => $target_path_resolved,
'action' => 'succeeded',
));
print_success(get_lang('addon_installed_successfully'));
} else {
scm_record_install_done($db, (int)$history_id, 'failed', 1, $workshop_error);
scm_log_content_install_action(array(
'addon_id' => (int)$addon_id,
'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '',
'content_type' => $install_method,
'home_id' => (int)$home_id,
'home_cfg_id' => (int)$home_info['home_cfg_id'],
'workshop_id' => $workshop_item_id,
'target_path' => $target_path_resolved,
'action' => 'failed',
'error' => $workshop_error,
));
print_failure($workshop_error);
}
echo "<p><a href=\"?m=addonsmanager&amp;p=user_addons&amp;home_id=$home_id&amp;mod_id=$mod_id&amp;ip=$ip&amp;port=$port\">".get_lang('back')."</a></p>";
return;
}
if ($install_method === 'post_script') {
$script_command = "cd " . escapeshellarg($home_info['home_path']) . " && /bin/bash -lc " . escapeshellarg((string)$post_script) . " ; echo __GSP_SCRIPT_EXIT:$?";
$script_output = $remote->exec($script_command);
$script_ok = is_string($script_output) && preg_match('/__GSP_SCRIPT_EXIT:(\d+)/', $script_output, $sm) && (int)$sm[1] === 0;
if ($script_ok) {
scm_record_install_done($db, (int)$history_id, 'installed', 0, trim((string)$script_output));
scm_upsert_manifest($db, $home_id, $addon_id, array(
'install_method' => $install_method,
'content_version' => $content_version,
'install_state' => 'installed',
'source_url' => '',
'installed_by' => $user_id,
));
scm_log_content_install_action(array('addon_id' => (int)$addon_id, 'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '', 'content_type' => $install_method, 'home_id' => (int)$home_id, 'home_cfg_id' => (int)$home_info['home_cfg_id'], 'action' => 'succeeded'));
print_success(get_lang('addon_installed_successfully'));
} else {
$error_msg = 'Script/action failed.';
scm_record_install_done($db, (int)$history_id, 'failed', 1, trim((string)$script_output));
scm_log_content_install_action(array('addon_id' => (int)$addon_id, 'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '', 'content_type' => $install_method, 'home_id' => (int)$home_id, 'home_cfg_id' => (int)$home_info['home_cfg_id'], 'action' => 'failed', 'error' => trim((string)$script_output)));
print_failure($error_msg);
}
echo "<p><a href=\"?m=addonsmanager&amp;p=user_addons&amp;home_id=$home_id&amp;mod_id=$mod_id&amp;ip=$ip&amp;port=$port\">".get_lang('back')."</a></p>";
return;
}
if ($install_method === 'config_edit' || $install_method === 'create_folder') {
$placeholder_map = scm_build_placeholder_map($home_info, array('exe_location' => (string)$server_xml->exe_location));
$target_template = trim((string)$addon_info['path']);
$resolved_path = scm_apply_placeholders($target_template, $placeholder_map);
if ($resolved_path === '' || strpos($resolved_path, '/') !== 0) {
$resolved_path = clean_path(rtrim($home_info['home_path'], '/') . '/' . ltrim($resolved_path, '/'));
}
$ok = false;
if ($install_method === 'create_folder') {
$ok = is_string($remote->exec("mkdir -p " . escapeshellarg($resolved_path) . " && echo __GSP_FOLDER_OK"));
} else {
$config_rule = trim((string)$addon_info['config_edit_rule']);
$dir = dirname($resolved_path);
$cmd = "mkdir -p " . escapeshellarg($dir) . " && touch " . escapeshellarg($resolved_path) . " && printf %s " . escapeshellarg($config_rule . PHP_EOL) . " >> " . escapeshellarg($resolved_path) . " && echo __GSP_CONFIG_OK";
$out = $remote->exec($cmd);
$ok = is_string($out) && strpos($out, '__GSP_CONFIG_OK') !== false;
}
if ($ok) {
scm_record_install_done($db, (int)$history_id, 'installed', 0, $install_method . '_ok');
scm_upsert_manifest($db, $home_id, $addon_id, array('install_method' => $install_method, 'content_version' => $content_version, 'install_state' => 'installed', 'source_url' => '', 'installed_by' => $user_id));
scm_log_content_install_action(array('addon_id' => (int)$addon_id, 'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '', 'content_type' => $install_method, 'home_id' => (int)$home_id, 'home_cfg_id' => (int)$home_info['home_cfg_id'], 'target_path' => $resolved_path, 'action' => 'succeeded'));
print_success(get_lang('addon_installed_successfully'));
} else {
scm_record_install_done($db, (int)$history_id, 'failed', 1, $install_method . '_failed');
scm_log_content_install_action(array('addon_id' => (int)$addon_id, 'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '', 'content_type' => $install_method, 'home_id' => (int)$home_id, 'home_cfg_id' => (int)$home_info['home_cfg_id'], 'target_path' => $resolved_path, 'action' => 'failed', 'error' => $install_method . '_failed'));
print_failure('Content action failed.');
}
echo "<p><a href=\"?m=addonsmanager&amp;p=user_addons&amp;home_id=$home_id&amp;mod_id=$mod_id&amp;ip=$ip&amp;port=$port\">".get_lang('back')."</a></p>";
return;
}
$download_action = ($install_method === 'download_file') ? "" : "uncompress";
$pid = $remote->start_file_download( $addon_info['url'], $home_info['home_path']."/".$addon_info['path'], $filename, $download_action, $post_script);
}
$headers = get_headers($url, 1);
@ -319,10 +485,22 @@ function exec_ogp_module() {
if(!$download_available)
{
print_failure(get_lang('failed_to_start_file_download'));
scm_log_content_install_action(array(
'addon_id' => (int)$addon_id,
'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '',
'content_type' => $install_method,
'home_id' => (int)$home_id,
'home_cfg_id' => (int)$home_info['home_cfg_id'],
'action' => 'failed',
'error' => 'failed_to_start_file_download',
));
}
elseif( $remote->is_file_download_in_progress($pid) === 1 )
{
print_success(get_lang_f('wait_while_decompressing', $filename));
if ($install_method === 'download_zip')
print_success(get_lang_f('wait_while_decompressing', $filename));
else
print_success(get_lang('install') . " " . $filename . "...");
echo "<p><a href=\"?m=addonsmanager&amp;p=addons&amp;state=refresh&amp;home_id=$home_id&amp;mod_id=$mod_id".
"&amp;ip=$ip&amp;port=$port&amp;addon_id=$addon_id&amp;pid=$pid\">".get_lang('refresh')."</a></p>";
$view->refresh("?m=addonsmanager&amp;p=addons&amp;state=refresh&amp;home_id=$home_id&amp;mod_id=$mod_id".
@ -344,6 +522,15 @@ function exec_ogp_module() {
'source_url' => $addon_info['url'],
'installed_by' => $user_id,
));
scm_log_content_install_action(array(
'addon_id' => (int)$addon_id,
'addon_name' => isset($addon_info['name']) ? $addon_info['name'] : '',
'content_type' => $install_method,
'home_id' => (int)$home_id,
'home_cfg_id' => (int)$home_info['home_cfg_id'],
'target_path' => isset($addon_info['path']) ? (string)$addon_info['path'] : '',
'action' => 'succeeded',
));
echo "<p><a href=\"?m=addonsmanager&amp;p=user_addons&amp;home_id=$home_id".
"&amp;mod_id=$mod_id&amp;ip=$ip&amp;port=$port\">".get_lang('back')."</a></p>";
$view->refresh("?m=addonsmanager&amp;p=user_addons&amp;home_id=$home_id".

View file

@ -41,32 +41,16 @@ function exec_ogp_module() {
$addon_type_labels = get_server_content_categories(); // key => label
$install_methods = scm_get_install_methods(); // install_method keys => labels
if (isset($_POST['create_addon']) AND isset($_POST['name']) AND $_POST['url']=="")
if (isset($_POST['create_addon']))
{
print_failure(get_lang("fill_the_url_address_to_a_compressed_file"));
}
elseif(isset($_POST['create_addon']) AND isset($_POST['url']) AND $_POST['name']=="")
{
print_failure(get_lang("fill_the_addon_name"));
}
elseif(isset($_POST['create_addon']) AND isset($_POST['name']) and isset($_POST['url']) and empty($_POST['addon_type']) )
{
print_failure(get_lang("select_an_addon_type"));
}
elseif(isset($_POST['create_addon']) AND isset($_POST['name']) and isset($_POST['url']) and isset($_POST['addon_type']) and empty($_POST['home_cfg_id']) )
{
print_failure(get_lang("select_a_game_type"));
}
elseif (isset($_POST['create_addon']) AND isset($_POST['name']) AND isset($_POST['url']) AND isset($_POST['addon_type']) and isset($_POST['home_cfg_id']) )
{
$valid_install_methods = array_keys($install_methods);
$fields['name'] = $_POST['name'];
$fields['url'] = $_POST['url'];
$fields['path'] = $_POST['path'];
$fields['addon_type'] = $_POST['addon_type'];
$fields['home_cfg_id'] = $_POST['home_cfg_id'];
$fields['post_script'] = $_POST['post_script'];
$fields['group_id'] = $_POST['group_id'];
$fields['name'] = isset($_POST['name']) ? trim((string)$_POST['name']) : '';
$fields['url'] = isset($_POST['url']) ? trim((string)$_POST['url']) : '';
$fields['path'] = isset($_POST['path']) ? trim((string)$_POST['path']) : '';
$fields['addon_type'] = isset($_POST['addon_type']) ? trim((string)$_POST['addon_type']) : '';
$fields['home_cfg_id'] = isset($_POST['home_cfg_id']) ? (int)$_POST['home_cfg_id'] : 0;
$fields['post_script'] = isset($_POST['post_script']) ? trim((string)$_POST['post_script']) : '';
$fields['group_id'] = isset($_POST['group_id']) ? (int)$_POST['group_id'] : 0;
$fields['install_method'] = in_array($_POST['install_method'], $valid_install_methods) ? $_POST['install_method'] : 'download_zip';
$fields['content_version'] = isset($_POST['content_version']) ? $_POST['content_version'] : '';
$fields['requires_stop'] = !empty($_POST['requires_stop']) ? 1 : 0;
@ -74,11 +58,46 @@ function exec_ogp_module() {
$fields['restart_after_install'] = !empty($_POST['restart_after_install']) ? 1 : 0;
$fields['is_cacheable'] = !empty($_POST['is_cacheable']) ? 1 : 0;
$fields['description'] = isset($_POST['description']) ? $_POST['description'] : '';
if( is_numeric($db->resultInsertId( 'addons', $fields )) )
$fields['workshop_item_id'] = isset($_POST['workshop_item_id']) ? trim((string)$_POST['workshop_item_id']) : '';
$fields['workshop_app_id'] = isset($_POST['workshop_app_id']) ? trim((string)$_POST['workshop_app_id']) : '';
$fields['target_path_template']= isset($_POST['target_path_template']) ? trim((string)$_POST['target_path_template']) : '';
$fields['optional_folder_name']= isset($_POST['optional_folder_name']) ? trim((string)$_POST['optional_folder_name']) : '';
$fields['config_edit_rule'] = isset($_POST['config_edit_rule']) ? trim((string)$_POST['config_edit_rule']) : '';
$fields['launch_param_additions'] = isset($_POST['launch_param_additions']) ? trim((string)$_POST['launch_param_additions']) : '';
if ($fields['name'] === '')
{
print_success(get_lang_f("addon_has_been_created",$_POST['name']));
if (isset($_POST['addon_id']) && (int)$_POST['addon_id'] > 0 && isset($_POST['edit']))
$db->query("DELETE FROM OGP_DB_PREFIXaddons WHERE addon_id=" . (int)$_POST['addon_id']);
print_failure(get_lang("fill_the_addon_name"));
}
elseif (empty($fields['addon_type']) || !in_array($fields['addon_type'], $addon_types))
{
print_failure(get_lang("select_an_addon_type"));
}
elseif (empty($fields['home_cfg_id']))
{
print_failure(get_lang("select_a_game_type"));
}
else
{
$validation_payload = array(
'url' => $fields['url'],
'path' => $fields['path'],
'workshop_item_id' => $fields['workshop_item_id'],
'target_path_template' => $fields['target_path_template'],
'post_script' => $fields['post_script'],
'config_edit_rule' => $fields['config_edit_rule'],
);
$validation_message = '';
if (!scm_validate_install_method_payload($fields['install_method'], $validation_payload, $validation_message))
{
print_failure($validation_message);
}
elseif (is_numeric($db->resultInsertId('addons', $fields)))
{
print_success(get_lang_f("addon_has_been_created", $fields['name']));
if (isset($_POST['addon_id']) && (int)$_POST['addon_id'] > 0 && isset($_POST['edit']))
$db->query("DELETE FROM OGP_DB_PREFIXaddons WHERE addon_id=" . (int)$_POST['addon_id']);
}
}
}
@ -97,6 +116,12 @@ function exec_ogp_module() {
$restart_after_install = isset($_POST['restart_after_install']) ? (int)$_POST['restart_after_install'] : 0;
$is_cacheable = isset($_POST['is_cacheable']) ? (int)$_POST['is_cacheable'] : 0;
$description = isset($_POST['description']) ? $_POST['description'] : "";
$workshop_item_id = isset($_POST['workshop_item_id']) ? $_POST['workshop_item_id'] : "";
$workshop_app_id = isset($_POST['workshop_app_id']) ? $_POST['workshop_app_id'] : "";
$target_path_template = isset($_POST['target_path_template']) ? $_POST['target_path_template'] : "";
$optional_folder_name = isset($_POST['optional_folder_name']) ? $_POST['optional_folder_name'] : "";
$config_edit_rule = isset($_POST['config_edit_rule']) ? $_POST['config_edit_rule'] : "";
$launch_param_additions = isset($_POST['launch_param_additions']) ? $_POST['launch_param_additions'] : "";
if (isset($_POST['addon_id']) && (int)$_POST['addon_id'] > 0 && isset($_POST['edit']))
{
@ -119,6 +144,12 @@ function exec_ogp_module() {
$restart_after_install = isset($addon_info['restart_after_install']) ? (int)$addon_info['restart_after_install'] : 0;
$is_cacheable = isset($addon_info['is_cacheable']) ? (int)$addon_info['is_cacheable'] : 0;
$description = isset($addon_info['description']) ? $addon_info['description'] : "";
$workshop_item_id = isset($addon_info['workshop_item_id']) ? $addon_info['workshop_item_id'] : "";
$workshop_app_id = isset($addon_info['workshop_app_id']) ? $addon_info['workshop_app_id'] : "";
$target_path_template = isset($addon_info['target_path_template']) ? $addon_info['target_path_template'] : "";
$optional_folder_name = isset($addon_info['optional_folder_name']) ? $addon_info['optional_folder_name'] : "";
$config_edit_rule = isset($addon_info['config_edit_rule']) ? $addon_info['config_edit_rule'] : "";
$launch_param_additions = isset($addon_info['launch_param_additions']) ? $addon_info['launch_param_additions'] : "";
}
?>
<form action="" method="post">
@ -131,7 +162,25 @@ function exec_ogp_module() {
<input type="text" value="<?php echo $name; ?>" name="name" size="85" title="<?php print_lang('addon_name_info'); ?>" />
</td>
</tr>
<tr>
<tr id="scm-row-install-method">
<td align="right">
<b><?php print_lang('content_type'); ?></b>
</td>
<td align="left">
<select name="install_method" id="scm-install-method">
<?php
$install_help = scm_get_install_method_help_text();
foreach ((array)$install_methods as $method_key => $method_label) {
$sel = ($method_key == $install_method) ? 'selected="selected"' : '';
$help = isset($install_help[$method_key]) ? $install_help[$method_key] : '';
echo '<option value="'.htmlspecialchars($method_key).'" data-help="'.htmlspecialchars($help, ENT_QUOTES, 'UTF-8').'" '.$sel.'>'.htmlspecialchars($method_label).'</option>'."\n";
}
?>
</select>
<div id="scm-install-method-help" style="color:#666;margin-top:4px;"></div>
</td>
</tr>
<tr id="scm-row-url">
<td align="right">
<b><?php print_lang('url'); ?></b>
</td>
@ -141,17 +190,50 @@ function exec_ogp_module() {
</tr>
<!-- Destination path must be relative to the game server home directory.
Path traversal (../) is not allowed; the agent enforces this. -->
<tr>
<tr id="scm-row-path">
<td align="right">
<b><?php print_lang('path'); ?></b>
<b id="scm-path-label"><?php print_lang('path'); ?></b>
</td>
<td align="left">
<input type="text" value="<?php echo $path; ?>" name="path" size="85" title="<?php print_lang('path_info'); ?>" />
</td>
</tr>
<tr>
<tr id="scm-row-workshop-id">
<td align="right">
<b><?php print_lang('post-script'); ?></b><br>
<b><?php print_lang('workshop_id'); ?></b>
</td>
<td align="left">
<input type="text" value="<?php echo htmlspecialchars($workshop_item_id, ENT_QUOTES, 'UTF-8'); ?>" name="workshop_item_id" size="85" placeholder="e.g. 450814997" />
</td>
</tr>
<tr id="scm-row-workshop-app-id">
<td align="right">
<b>Workshop App ID</b>
</td>
<td align="left">
<input type="text" value="<?php echo htmlspecialchars($workshop_app_id, ENT_QUOTES, 'UTF-8'); ?>" name="workshop_app_id" size="85" placeholder="Optional override, e.g. 221100" />
</td>
</tr>
<tr id="scm-row-target-path-template">
<td align="right">
<b><?php print_lang('target_path_template'); ?></b>
</td>
<td align="left">
<input type="text" value="<?php echo htmlspecialchars($target_path_template, ENT_QUOTES, 'UTF-8'); ?>" name="target_path_template" size="85" placeholder="{SERVER_ROOT}/mods/{WORKSHOP_ID}" />
<small style="color:#666;">Supported placeholders: {HOME_ID}, {SERVER_ROOT}, {GAME_ROOT}, {WORKSHOP_ID}, {WORKSHOP_APP_ID}, {STEAM_APP_ID}</small>
</td>
</tr>
<tr id="scm-row-optional-folder-name">
<td align="right">
<b><?php print_lang('optional_folder_name'); ?></b>
</td>
<td align="left">
<input type="text" value="<?php echo htmlspecialchars($optional_folder_name, ENT_QUOTES, 'UTF-8'); ?>" name="optional_folder_name" size="85" placeholder="@MyWorkshopMod" />
</td>
</tr>
<tr id="scm-row-post-script">
<td align="right">
<b>Post-Install Script / Action</b><br>
<u><?php print_lang('replacements'); ?></u><br>
%home_path%<br>
%home_name%<br>
@ -166,6 +248,22 @@ function exec_ogp_module() {
<textarea name="post_script" style="width:99%;height:175px;" title="<?php print_lang('post-script_info'); ?>" ><?php echo strip_real_escape_string($post_script); ?></textarea>
</td>
</tr>
<tr id="scm-row-config-edit-rule">
<td align="right">
<b><?php print_lang('config_edit_rule'); ?></b>
</td>
<td align="left">
<textarea name="config_edit_rule" style="width:99%;height:90px;" placeholder="Text/rules to append or apply to the target config."><?php echo htmlspecialchars($config_edit_rule, ENT_QUOTES, 'UTF-8'); ?></textarea>
</td>
</tr>
<tr id="scm-row-launch-param-additions">
<td align="right">
<b><?php print_lang('launch_param_additions'); ?></b>
</td>
<td align="left">
<input type="text" value="<?php echo htmlspecialchars($launch_param_additions, ENT_QUOTES, 'UTF-8'); ?>" name="launch_param_additions" size="85" placeholder="-mod=@myMod;@anotherMod" />
</td>
</tr>
<tr>
<td align="right">
<b><?php print_lang('select_game_type'); ?></b>
@ -241,23 +339,6 @@ function exec_ogp_module() {
</select>
</td>
</tr>
<!-- ── Phase 2 fields ────────────────────────────────────────────────── -->
<tr>
<td align="right">
<b>Install Method</b>
</td>
<td align="left">
<select name="install_method">
<?php
foreach ((array)$install_methods as $method_key => $method_label) {
$sel = ($method_key == $install_method) ? 'selected="selected"' : '';
echo '<option value="'.htmlspecialchars($method_key).'" '.$sel.'>'.htmlspecialchars($method_label).'</option>'."\n";
}
?>
</select>
<small style="color:#666;"> The mechanism used to deliver this content to the server.</small>
</td>
</tr>
<tr>
<td align="right">
<b>Content Version</b>
@ -313,7 +394,6 @@ function exec_ogp_module() {
</small>
</td>
</tr>
<!-- ── end Phase 2 fields ─────────────────────────────────────────────── -->
<tr>
<td colspan="2" align="center">
<?php

View file

@ -23,8 +23,8 @@
// Module general information
$module_title = "Server Content Manager";
$module_version = "2.2";
$db_version = 4;
$module_version = "2.3";
$db_version = 5;
$module_required = TRUE;
$module_menus = array(
array( 'subpage' => 'addons_manager', 'name' => 'Server Content Manager', 'group' => 'admin' )
@ -165,4 +165,35 @@ $install_queries[3] = array(
return true;
},
);
// ── db_version 5 : content-type specific metadata for Workshop/config/folder actions ──
$install_queries[4] = array(
function ($db) {
$prefix = OGP_DB_PREFIX;
$new_columns = array(
'workshop_item_id' => "VARCHAR(64) NULL AFTER `description`",
'workshop_app_id' => "VARCHAR(32) NULL AFTER `workshop_item_id`",
'target_path_template' => "VARCHAR(255) NULL AFTER `workshop_app_id`",
'optional_folder_name' => "VARCHAR(255) NULL AFTER `target_path_template`",
'config_edit_rule' => "TEXT NULL AFTER `optional_folder_name`",
'launch_param_additions' => "VARCHAR(255) NULL AFTER `config_edit_rule`",
);
foreach ($new_columns as $col => $definition) {
$escaped_col = $db->realEscapeSingle($col);
$escaped_table = $db->realEscapeSingle($prefix . 'addons');
$check = $db->resultQuery(
"SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = '{$escaped_table}'
AND COLUMN_NAME = '{$escaped_col}'"
);
if (empty($check)) {
if (!$db->query("ALTER TABLE `{$prefix}addons` ADD COLUMN `{$col}` {$definition}")) {
return false;
}
}
}
return true;
},
);
?>

View file

@ -255,15 +255,146 @@ function scm_get_cache_mode($db)
function scm_get_install_methods()
{
return array(
'download_zip' => 'Download & extract archive (.zip / .tar.gz)',
'download_file' => 'Download single file (no extraction)',
'post_script' => 'Run post-script only (no download)',
'steam_workshop' => 'Steam Workshop (via agent SteamCMD)',
'minecraft_jar' => 'Minecraft server jar / version switcher',
'profile_copy' => 'Copy stored profile directory',
'download_zip' => 'Compressed file download',
'download_file' => 'Direct file download',
'steam_workshop' => 'Steam Workshop item',
'post_script' => 'Script/action only',
'config_edit' => 'Config edit only',
'create_folder' => 'Folder/create path only',
);
}
function scm_get_install_method_help_text()
{
return array(
'download_zip' => 'Downloads and extracts an archive into the target path.',
'download_file' => 'Downloads a single file to the target path without extraction.',
'steam_workshop' => 'Downloads/updates a Steam Workshop item and applies it to the server path.',
'post_script' => 'Runs only the post-install script/action body (no download).',
'config_edit' => 'Applies config edit rules to a target config file/path.',
'create_folder' => 'Creates the target directory path only.',
);
}
function scm_get_install_method_required_fields()
{
return array(
'download_zip' => array('url', 'path'),
'download_file' => array('url', 'path'),
'steam_workshop' => array('workshop_item_id', 'target_path_template'),
'post_script' => array('post_script'),
'config_edit' => array('path', 'config_edit_rule'),
'create_folder' => array('path'),
);
}
function scm_get_install_method_validation_errors()
{
return array(
'url' => 'Please enter a download URL.',
'workshop_item_id' => 'Please enter a Workshop ID.',
'target_path_template' => 'Please select a target install path.',
'post_script' => 'Please enter a script/action body.',
'config_edit_rule' => 'Please enter a config edit rule.',
'path' => 'Please select a target install path.',
);
}
function scm_get_install_method_default($value = '')
{
$methods = scm_get_install_methods();
$value = trim((string)$value);
return isset($methods[$value]) ? $value : 'download_zip';
}
function scm_validate_install_method_payload($install_method, array $payload, &$message = '')
{
$install_method = scm_get_install_method_default($install_method);
$required = scm_get_install_method_required_fields();
$errors = scm_get_install_method_validation_errors();
if (!isset($required[$install_method])) {
$message = 'Invalid install/content type selected.';
return false;
}
foreach ($required[$install_method] as $field) {
$value = isset($payload[$field]) ? trim((string)$payload[$field]) : '';
if ($value === '') {
$message = isset($errors[$field]) ? $errors[$field] : 'Missing required field.';
return false;
}
}
if ($install_method === 'steam_workshop') {
$wid = isset($payload['workshop_item_id']) ? trim((string)$payload['workshop_item_id']) : '';
if ($wid === '' || !preg_match('/^[0-9]+$/', $wid)) {
$message = 'Please enter a Workshop ID.';
return false;
}
}
$message = '';
return true;
}
function scm_build_placeholder_map(array $home_info, array $server_context = array(), array $overrides = array())
{
$home_id = (int)(isset($home_info['home_id']) ? $home_info['home_id'] : 0);
$server_root = rtrim(clean_path((string)(isset($home_info['home_path']) ? $home_info['home_path'] : '')), '/');
$game_root = $server_root;
if (!empty($server_context['exe_location'])) {
$exe_location = clean_path((string)$server_context['exe_location']);
$exe_dir = dirname($exe_location);
if ($exe_dir !== '.' && $exe_dir !== '/') {
$game_root = clean_path($server_root . '/' . ltrim($exe_dir, '/'));
}
}
$map = array(
'{HOME_ID}' => (string)$home_id,
'{SERVER_ROOT}' => $server_root,
'{GAME_ROOT}' => $game_root,
'{WORKSHOP_ID}' => '',
'{WORKSHOP_APP_ID}' => '',
'{STEAM_APP_ID}' => '',
);
foreach ($overrides as $key => $value) {
$token = '{' . strtoupper(trim((string)$key, '{}')) . '}';
$map[$token] = (string)$value;
}
return $map;
}
function scm_apply_placeholders($template, array $placeholder_map)
{
$template = (string)$template;
if ($template === '') {
return '';
}
return str_replace(array_keys($placeholder_map), array_values($placeholder_map), $template);
}
function scm_content_logs_dir()
{
return dirname(__FILE__) . '/logs';
}
function scm_content_log_file()
{
return scm_content_logs_dir() . '/content_install.log';
}
function scm_log_content_install_action(array $context)
{
$dir = scm_content_logs_dir();
if (!is_dir($dir)) {
@mkdir($dir, 0775, true);
}
$context['logged_at'] = date('Y-m-d H:i:s');
$line = json_encode($context);
if ($line === false) {
$line = '{"logged_at":"' . date('Y-m-d H:i:s') . '","error":"json_encode_failed"}';
}
@error_log($line . PHP_EOL, 3, scm_content_log_file());
return true;
}
/**
* Idempotently ensures the Phase 2 schema is present.
* Called from pages that use manifest / history data so that existing
@ -290,6 +421,12 @@ function scm_ensure_phase2_schema($db)
'restart_after_install' => "TINYINT(1) NOT NULL DEFAULT 0",
'is_cacheable' => "TINYINT(1) NOT NULL DEFAULT 0",
'description' => "TEXT NULL",
'workshop_item_id' => "VARCHAR(64) NULL",
'workshop_app_id' => "VARCHAR(32) NULL",
'target_path_template' => "VARCHAR(255) NULL",
'optional_folder_name' => "VARCHAR(255) NULL",
'config_edit_rule' => "TEXT NULL",
'launch_param_additions'=> "VARCHAR(255) NULL",
);
foreach ($new_columns as $col => $definition) {
$escaped_col = $db->realEscapeSingle($col);
@ -489,4 +626,3 @@ function scm_upsert_manifest($db, $home_id, $addon_id, array $fields = array())
updated_at = NOW()"
);
}

View file

@ -67,7 +67,7 @@ function scm_workshop_filter_existing_ids($db, $home_id, array $item_ids)
return array_values($allowed);
}
function scm_workshop_write_manifest_and_run($db, array $home_info, $server_xml, $action, array $item_ids, &$error = '')
function scm_workshop_write_manifest_and_run($db, array $home_info, $server_xml, $action, array $item_ids, &$error = '', array $extra_manifest = array())
{
$error = '';
if (empty($item_ids)) {
@ -102,6 +102,9 @@ function scm_workshop_write_manifest_and_run($db, array $home_info, $server_xml,
'workshop_app_id' => scm_extract_workshop_app_id($server_xml),
'items' => array_values($item_ids),
);
if (!empty($extra_manifest)) {
$manifest['extra'] = $extra_manifest;
}
$manifest_json = json_encode($manifest);
if ($manifest_json === false) {
$error = 'Failed to encode workshop manifest JSON.';
@ -274,4 +277,3 @@ function scm_workshop_handle_action($db, array $home_info, $user_id, $action, $r
$message = 'Invalid workshop action.';
return false;
}

View file

@ -577,3 +577,61 @@ function sw_apply_detected_profile_defaults($db, array $profile, array $detected
);
return $updated;
}
function steam_workshop_resolve_paths($db, array $home_info, $workshop_id, $target_path_template = '', $optional_folder_name = '', $workshop_app_id_override = '')
{
$profile = sw_get_profile_for_home($db, (int)$home_info['home_id']);
if (!$profile || !is_array($profile)) {
return array('ok' => false, 'error' => 'No enabled Steam Workshop profile for this server home.');
}
$workshop_id = trim((string)$workshop_id);
if ($workshop_id === '' || !preg_match('/^[0-9]+$/', $workshop_id)) {
return array('ok' => false, 'error' => 'Invalid Workshop ID.');
}
$workshop_app_id = trim((string)$workshop_app_id_override);
if ($workshop_app_id === '') {
$workshop_app_id = (string)$profile['workshop_app_id'];
}
$folder_name = trim((string)$optional_folder_name);
if ($folder_name === '') {
$folder_name = '@' . $workshop_id;
}
$vars = array(
'HOME_ID' => (int)$home_info['home_id'],
'SERVER_ROOT' => rtrim((string)$home_info['home_path'], '/'),
'GAME_ROOT' => rtrim((string)$home_info['home_path'], '/'),
'WORKSHOP_ID' => $workshop_id,
'WORKSHOP_APP_ID' => $workshop_app_id,
'STEAM_APP_ID' => (string)$profile['steam_app_id'],
'FOLDER_NAME' => $folder_name,
'MOD_FOLDER' => $folder_name,
);
$target_template = trim((string)$target_path_template);
if ($target_template === '') {
$target_template = !empty($profile['install_path_template']) ? (string)$profile['install_path_template'] : '{SERVER_ROOT}/{MOD_FOLDER}';
}
$resolved_target = sw_apply_template($target_template, $vars);
return array(
'ok' => true,
'profile' => $profile,
'workshop_id' => $workshop_id,
'workshop_app_id' => $workshop_app_id,
'steam_app_id' => (string)$profile['steam_app_id'],
'folder_name' => $folder_name,
'target_path_template' => $target_template,
'target_path_resolved' => $resolved_target,
'vars' => $vars,
);
}
function steam_workshop_download_item($db, array $home_info, $workshop_id, $target_path_template = '', array $options = array())
{
$optional_folder_name = isset($options['optional_folder_name']) ? $options['optional_folder_name'] : '';
$workshop_app_id = isset($options['workshop_app_id']) ? $options['workshop_app_id'] : '';
return steam_workshop_resolve_paths($db, $home_info, $workshop_id, $target_path_template, $optional_folder_name, $workshop_app_id);
}
function steam_workshop_install_item_to_home($db, array $home_info, $workshop_id, $target_path_template = '', array $options = array())
{
return steam_workshop_download_item($db, $home_info, $workshop_id, $target_path_template, $options);
}

View file

@ -1357,6 +1357,10 @@ function api_litefm()
function api_addonsmanager()
{
global $request, $db, $user_info;
require_once(MODULES.'addonsmanager/server_content_helpers.php');
require_once(MODULES.'addonsmanager/workshop_action.php');
if (file_exists(MODULES.'steam_workshop/includes/functions.php'))
require_once(MODULES.'steam_workshop/includes/functions.php');
if($db->isModuleInstalled('addonsmanager') === FALSE)
return array("status" => '349', "message" => "This function is not available because the module is not installed.");
@ -1427,9 +1431,22 @@ function api_addonsmanager()
$addon_info = $addons_rows[0];
$install_method = scm_get_install_method_default(isset($addon_info['install_method']) ? $addon_info['install_method'] : 'download_zip');
$validation_payload = array(
'url' => isset($addon_info['url']) ? $addon_info['url'] : '',
'path' => isset($addon_info['path']) ? $addon_info['path'] : '',
'workshop_item_id' => isset($addon_info['workshop_item_id']) ? $addon_info['workshop_item_id'] : '',
'target_path_template' => isset($addon_info['target_path_template']) ? $addon_info['target_path_template'] : '',
'post_script' => isset($addon_info['post_script']) ? $addon_info['post_script'] : '',
'config_edit_rule' => isset($addon_info['config_edit_rule']) ? $addon_info['config_edit_rule'] : '',
);
$validation_message = '';
if (!scm_validate_install_method_payload($install_method, $validation_payload, $validation_message))
return array("status" => '422', "message" => $validation_message);
$url = $addon_info['url'];
$filename = basename($url);
$post_script = '';
if($addon_info['post_script'] != "")
{
$addon_info['post_script'] = strip_real_escape_string($addon_info['post_script']);
@ -1461,7 +1478,45 @@ function api_addonsmanager()
$post_script = preg_replace( "/\%incremental\%/i", $home_info['incremental'], $post_script);
}
$pid = $remote->start_file_download($addon_info['url'], $home_info['home_path']."/".$addon_info['path'], $filename, "uncompress", $post_script);
if ($install_method === 'steam_workshop') {
scm_ensure_workshop_schema($db);
$workshop_item_id = trim((string)$addon_info['workshop_item_id']);
$workshop_error = '';
$extra_manifest = array(
'addon_id' => (int)$addon_id,
'target_path_template' => isset($addon_info['target_path_template']) ? (string)$addon_info['target_path_template'] : '',
'optional_folder_name' => isset($addon_info['optional_folder_name']) ? (string)$addon_info['optional_folder_name'] : '',
'config_edit_rule' => isset($addon_info['config_edit_rule']) ? (string)$addon_info['config_edit_rule'] : '',
'launch_param_additions' => isset($addon_info['launch_param_additions']) ? (string)$addon_info['launch_param_additions'] : '',
);
$ok = scm_workshop_write_manifest_and_run($db, $home_info, $server_xml, 'install', array($workshop_item_id), $workshop_error, $extra_manifest);
if ($ok)
return array("status" => "200", "message" => "Workshop content installed successfully.");
return array("status" => "342", "message" => "Workshop install failed: ".$workshop_error);
}
if ($install_method === 'post_script') {
$output = $remote->exec("cd " . escapeshellarg($home_info['home_path']) . " && /bin/bash -lc " . escapeshellarg((string)$post_script) . " ; echo __GSP_SCRIPT_EXIT:$?");
if (is_string($output) && preg_match('/__GSP_SCRIPT_EXIT:(\d+)/', $output, $m) && (int)$m[1] === 0)
return array("status" => "200", "message" => "Script/action completed.");
return array("status" => "342", "message" => "Script/action failed.");
}
if ($install_method === 'config_edit' || $install_method === 'create_folder') {
$path = trim((string)$addon_info['path']);
if ($path === '')
return array("status" => "422", "message" => "Please select a target install path.");
$target = (strpos($path, '/') === 0) ? $path : clean_path(rtrim($home_info['home_path'], '/') . '/' . ltrim($path, '/'));
if ($install_method === 'create_folder')
$out = $remote->exec("mkdir -p " . escapeshellarg($target) . " && echo __GSP_FOLDER_OK");
else {
$rule = trim((string)$addon_info['config_edit_rule']);
$out = $remote->exec("mkdir -p " . escapeshellarg(dirname($target)) . " && touch " . escapeshellarg($target) . " && printf %s " . escapeshellarg($rule . PHP_EOL) . " >> " . escapeshellarg($target) . " && echo __GSP_CONFIG_OK");
}
if (is_string($out))
return array("status" => "200", "message" => "Content action completed.");
return array("status" => "342", "message" => "Content action failed.");
}
$download_action = ($install_method === 'download_file') ? "" : "uncompress";
$pid = $remote->start_file_download($addon_info['url'], $home_info['home_path']."/".$addon_info['path'], $filename, $download_action, $post_script);
if($pid > 0)
{
$status = "200";