diff --git a/modules/steam_workshop/functions.php b/modules/steam_workshop/functions.php index 48b8c229..65001899 100644 --- a/modules/steam_workshop/functions.php +++ b/modules/steam_workshop/functions.php @@ -21,62 +21,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ -function sw_text($key, ...$args) -{ - $strings = array( - 'no_game_servers_assigned' => 'No game servers are assigned to your account.', - 'uninstall_mods' => 'Uninstall mods', - 'install_mods' => 'Install mods', - 'back' => 'Back', - 'settings_updated' => 'Settings updated.', - 'failed_reading_xml_file' => 'Failed reading XML file: %s', - 'mod_id_does_not_exists_in_home' => 'Mod ID %s does not exist in home %s.', - 'mod_key_not_found_from_xml' => 'Mod key %s was not found in the XML.', - 'unable_to_get_os_from_game_key' => 'Unable to determine OS from game key %s.', - 'workshop_configuration_not_found' => 'Workshop configuration not found for this game/mod.', - 'no_workshop_configuration_available_for_this_game' => 'No workshop configuration is available for this game.', - 'workshop_configuration_file_has_bad_format' => 'Workshop configuration file has a bad format.', - 'remote_server_offline' => 'Remote server is offline.', - 'update_in_progress' => 'Update in progress...', - 'refresh_steam_workshop_status' => 'Refresh Steam Workshop status', - 'update_completed' => 'Update completed.', - 'game_home_not_found' => 'Game home not found.', - 'mod_does_not_belong_to_workshop' => 'Mod %s does not belong to this workshop.', - 'mod_installation_started' => 'Mod installation started.', - 'invalid_mod_id' => 'Invalid workshop ID.', - 'failed_uninstalling_mod' => 'Failed uninstalling mod %s.', - 'failed_to_start_steam_workshop' => 'Failed to start Steam Workshop.', - 'connection_error' => 'Connection error.', - 'select_at_least_one_mod_or_enter_mod_id' => 'Select at least one mod or enter a mod ID.', - 'install_mod' => 'Install mod(s)', - 'show_mod_info' => 'Show mod info', - 'select_game' => 'Select a game', - 'save_config' => 'Save config', - 'remove_mods' => 'Remove selected mods', - 'mod_installation_failed' => 'Mod installation failed.', - 'there_are_no_mods_installed_on_this_game_server' => 'There are no mods installed on this game server.', - 'installed_mods' => 'Installed mods', - 'no_mods_found' => 'No mods found.', - 'name' => 'Name', - 'status' => 'Status', - 'installed' => 'Installed', - 'pending' => 'Pending', - 'remove' => 'Remove', - 'add_mod' => 'Add mod', - 'workshop_id' => 'Workshop ID', - 'add' => 'Add', - 'collection_id' => 'Collection ID', - 'actions' => 'Actions', - ); - - if (!array_key_exists($key, $strings)) { - return $key; - } - - $text = $strings[$key]; - return empty($args) ? $text : vsprintf($text, $args); -} - function create_drop_box_from_array_onchange($input_array,$listname,$current_value = "") { $only_one = count($input_array) == 1; @@ -107,24 +51,6 @@ function create_drop_box_from_array_onchange($input_array,$listname,$current_val return $retval; } -function create_drop_box_from_array($input_array,$listname,$current_value = "") -{ - $retval = "\n"; - return $retval; -} - function get_mod_names_list($mods_list, $xml_mods) { $mod_names = ""; diff --git a/modules/steam_workshop/lib/Workshop.php b/modules/steam_workshop/lib/Workshop.php deleted file mode 100644 index fdb08cdd..00000000 --- a/modules/steam_workshop/lib/Workshop.php +++ /dev/null @@ -1,417 +0,0 @@ -file = $file; - $dir = dirname($file); - if (!is_dir($dir)) { - mkdir($dir, 0775, true); - } - } - - public function all() - { - $data = $this->readFile(); - return is_array($data) ? $data : []; - } - - public function get($homeId) - { - $all = $this->all(); - return isset($all[$homeId]) ? $all[$homeId] : null; - } - - public function put($homeId, array $config) - { - $all = $this->all(); - $all[$homeId] = $config; - $this->writeFile($all); - } - - public function delete($homeId) - { - $all = $this->all(); - if (isset($all[$homeId])) { - unset($all[$homeId]); - $this->writeFile($all); - } - } - - private function readFile() - { - if (!is_file($this->file)) { - return []; - } - $fh = fopen($this->file, 'c+'); - if ($fh === false) { - return []; - } - flock($fh, LOCK_SH); - $raw = stream_get_contents($fh); - flock($fh, LOCK_UN); - fclose($fh); - $data = json_decode($raw, true); - return is_array($data) ? $data : []; - } - - private function writeFile(array $data) - { - $fh = fopen($this->file, 'c+'); - if ($fh === false) { - return; - } - flock($fh, LOCK_EX); - ftruncate($fh, 0); - rewind($fh); - fwrite($fh, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - fflush($fh); - flock($fh, LOCK_UN); - fclose($fh); - } -} - -class WorkshopStateStore -{ - private $dir; - - public function __construct($dir) - { - $this->dir = rtrim($dir, '/'); - if (!is_dir($this->dir)) { - mkdir($this->dir, 0775, true); - } - } - - public function get($homeId) - { - $path = $this->statePath($homeId); - if (!is_file($path)) { - return ['items' => [], 'last_sync' => null, 'last_status' => null]; - } - $raw = file_get_contents($path); - $data = json_decode($raw, true); - if (!is_array($data)) { - return ['items' => [], 'last_sync' => null, 'last_status' => null]; - } - if (!isset($data['items'])) { - $data['items'] = []; - } - return $data; - } - - public function put($homeId, array $state) - { - $path = $this->statePath($homeId); - $payload = json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - file_put_contents($path, $payload); - } - - public function updateItem($homeId, $itemId, array $itemState) - { - $state = $this->get($homeId); - $state['items'][$itemId] = $itemState; - $this->put($homeId, $state); - } - - private function statePath($homeId) - { - return $this->dir . '/state_' . $homeId . '.json'; - } -} - -class WorkshopLock -{ - private $fh; - - public function __construct($lockPath) - { - $dir = dirname($lockPath); - if (!is_dir($dir)) { - mkdir($dir, 0775, true); - } - $this->fh = fopen($lockPath, 'c'); - } - - public function acquire() - { - if (!$this->fh) { - return false; - } - return flock($this->fh, LOCK_EX | LOCK_NB); - } - - public function release() - { - if ($this->fh) { - flock($this->fh, LOCK_UN); - } - } -} - -class WorkshopResolver -{ - public function resolveItems(array $config) - { - $items = array_map('trim', isset($config['workshop_item_ids']) ? (array)$config['workshop_item_ids'] : []); - $collections = array_map('trim', isset($config['collection_ids']) ? (array)$config['collection_ids'] : []); - - if (!empty($collections)) { - foreach ($collections as $collectionId) { - $expanded = $this->expandCollection($collectionId); - $items = array_merge($items, $expanded); - } - } - - $items = array_values(array_unique(array_filter($items, 'strlen'))); - return $items; - } - - private function expandCollection($collectionId) - { - $payload = http_build_query([ - 'collectioncount' => 1, - 'publishedfileids[0]' => $collectionId, - ]); - - $context = stream_context_create([ - 'http' => [ - 'method' => 'POST', - 'header' => "Content-Type: application/x-www-form-urlencoded", - 'content' => $payload, - 'timeout' => 10, - ], - ]); - - $json = @file_get_contents('https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/', false, $context); - if ($json === false) { - return []; - } - $data = json_decode($json, true); - if (!isset($data['response']['collectiondetails'][0]['children'])) { - return []; - } - $children = $data['response']['collectiondetails'][0]['children']; - $ids = []; - foreach ($children as $child) { - if (isset($child['publishedfileid'])) { - $ids[] = $child['publishedfileid']; - } - } - return $ids; - } - - public function fetchItemDetails(array $itemIds) - { - if (empty($itemIds)) { - return []; - } - $payload = ['itemcount' => count($itemIds)]; - $index = 0; - foreach ($itemIds as $id) { - $payload['publishedfileids[' . $index . ']'] = $id; - $index++; - } - - $context = stream_context_create([ - 'http' => [ - 'method' => 'POST', - 'header' => "Content-Type: application/x-www-form-urlencoded", - 'content' => http_build_query($payload), - 'timeout' => 10, - ], - ]); - - $json = @file_get_contents('https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/', false, $context); - if ($json === false) { - return []; - } - $data = json_decode($json, true); - if (!isset($data['response']['publishedfiledetails'])) { - return []; - } - $details = []; - foreach ($data['response']['publishedfiledetails'] as $item) { - $details[$item['publishedfileid']] = $item; - } - return $details; - } -} - -class WorkshopSyncResult -{ - public $success; - public $message; - public $updatedItems; - public $skippedItems; - - public function __construct($success, $message, array $updatedItems = [], array $skippedItems = []) - { - $this->success = $success; - $this->message = $message; - $this->updatedItems = $updatedItems; - $this->skippedItems = $skippedItems; - } -} - -class WorkshopSyncService -{ - private $remote; - private $homeCfg; - private $configStore; - private $stateStore; - private $resolver; - - public function __construct($remote, array $homeCfg, WorkshopConfigStore $configStore, WorkshopStateStore $stateStore, WorkshopResolver $resolver) - { - $this->remote = $remote; - $this->homeCfg = $homeCfg; - $this->configStore = $configStore; - $this->stateStore = $stateStore; - $this->resolver = $resolver; - } - - public function sync($homeId) - { - $config = $this->configStore->get($homeId); - if (!$config) { - return new WorkshopSyncResult(false, 'No workshop configuration found for this server.'); - } - - $itemIds = $this->resolver->resolveItems($config); - if (empty($itemIds)) { - return new WorkshopSyncResult(true, 'No workshop items configured.', [], []); - } - - $details = $this->resolver->fetchItemDetails($itemIds); - $state = $this->stateStore->get($homeId); - $needsUpdate = []; - $skipped = []; - - foreach ($itemIds as $id) { - $remoteUpdated = isset($details[$id]['time_updated']) ? (int)$details[$id]['time_updated'] : null; - $local = isset($state['items'][$id]) ? $state['items'][$id] : null; - $localSeen = $local && isset($local['last_seen_manifest_id']) ? (int)$local['last_seen_manifest_id'] : null; - if ($remoteUpdated !== null && $localSeen !== null && $remoteUpdated <= $localSeen) { - $skipped[] = $id; - continue; - } - $needsUpdate[] = $id; - } - - if (empty($needsUpdate)) { - $state['last_sync'] = time(); - $state['last_status'] = 'up-to-date'; - $this->stateStore->put($homeId, $state); - return new WorkshopSyncResult(true, 'All workshop items are up-to-date.', [], $skipped); - } - - $modsFullPath = $this->modsPath($config); - $paths = $this->buildPaths($config); - $modNamesList = implode(',', $needsUpdate); - - $result = $this->remote->steam_workshop( - $homeId, - $modsFullPath, - $config['workshop_app_id'], - implode(',', $needsUpdate), - $config['regex'], - (int)$config['mods_backreference_index'], - $config['variable'], - $config['place_after'], - $config['mod_string'], - $config['string_separator'], - $paths['config_file_path'], - $config['post_install'], - $modNamesList, - $config['anonymous_login'] ? '1' : '0', - $config['steam_username'], - $config['steam_password'], - $config['download_method'], - '', - '' - ); - - if ($result !== 1) { - return new WorkshopSyncResult(false, 'Workshop sync failed to start (agent error code ' . $result . ').'); - } - - $now = time(); - foreach ($needsUpdate as $id) { - $state['items'][$id] = [ - 'last_seen_manifest_id' => isset($details[$id]['time_updated']) ? (int)$details[$id]['time_updated'] : $now, - 'last_downloaded_at' => $now, - 'local_path' => $modsFullPath, - 'deploy_path' => $paths['deploy_destination'], - 'last_error' => null, - ]; - } - $state['last_sync'] = $now; - $state['last_status'] = 'started'; - $this->stateStore->put($homeId, $state); - - return new WorkshopSyncResult(true, 'Workshop sync started.', $needsUpdate, $skipped); - } - - private function modsPath(array $config) - { - if (!empty($config['staging_path'])) { - return clean_path($config['staging_path']); - } - return clean_path($this->homeCfg['home_path'] . '/workshop'); - } - - private function buildPaths(array $config) - { - $serverRoot = rtrim($this->homeCfg['home_path'], '/'); - $deployDest = !empty($config['deploy_destination']) ? clean_path($config['deploy_destination']) : ($serverRoot . '/mods'); - $configPath = !empty($config['filepath']) ? clean_path($config['filepath']) : ($serverRoot . '/config.cfg'); - return [ - 'deploy_destination' => $deployDest, - 'config_file_path' => $configPath, - ]; - } -} - -function workshop_build_default_config($homeCfg, $modXml, $settings) -{ - $modsPath = ($modXml && isset($modXml->mods_path)) ? (string)$modXml->mods_path : 'mods'; - $configNode = $modXml && isset($modXml->config) ? $modXml->config : null; - - return [ - 'workshop_app_id' => ($modXml && isset($modXml->workshop_id)) ? (string)$modXml->workshop_id : '', - 'download_method' => ($modXml && isset($modXml->download_method)) ? (string)$modXml->download_method : 'steamcmd', - 'deploy_mode' => 'copy', - 'deploy_destination' => clean_path($homeCfg['home_path'] . '/' . $modsPath), - 'staging_path' => clean_path($homeCfg['home_path'] . '/' . $modsPath), - 'mods_path' => clean_path($homeCfg['home_path'] . '/' . $modsPath), - 'regex' => ($configNode && isset($configNode->regex)) ? (string)$configNode->regex : '', - 'mods_backreference_index' => ($configNode && isset($configNode->mods_backreference_index)) ? (int)$configNode->mods_backreference_index : 1, - 'variable' => ($configNode && isset($configNode->variable)) ? (string)$configNode->variable : '', - 'place_after' => ($configNode && isset($configNode->place_after)) ? (string)$configNode->place_after : '', - 'mod_string' => ($configNode && isset($configNode->mod_string)) ? (string)$configNode->mod_string : '%workshop_mod_id%', - 'string_separator' => ($configNode && isset($configNode->string_separator)) ? (string)$configNode->string_separator : ';', - 'filepath' => ($configNode && isset($configNode->filepath)) ? clean_path($homeCfg['home_path'] . '/' . $configNode->filepath) : '', - 'post_install' => ($modXml && isset($modXml->post_install)) ? (string)$modXml->post_install : '', - 'uninstall' => ($modXml && isset($modXml->uninstall)) ? (string)$modXml->uninstall : '', - 'anonymous_login' => ($modXml && isset($modXml->anonymous_login)) ? ((string)$modXml->anonymous_login === '1') : true, - 'steam_username' => isset($settings['steam_user']) ? $settings['steam_user'] : '', - 'steam_password' => isset($settings['steam_pass']) ? $settings['steam_pass'] : '', - 'workshop_item_ids' => [], - 'collection_ids' => [], - 'check_on_start' => true, - 'periodic_check_minutes' => null, - 'apply_updates' => 'on_start_only', - ]; -} diff --git a/modules/steam_workshop/main.php b/modules/steam_workshop/main.php index 3a357ef1..87d9f7c2 100644 --- a/modules/steam_workshop/main.php +++ b/modules/steam_workshop/main.php @@ -21,167 +21,312 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ -require_once('includes/lib_remote.php'); -require_once('modules/config_games/server_config_parser.php'); -require_once('modules/steam_workshop/lib/Workshop.php'); -require_once('modules/steam_workshop/functions.php'); +require_once("includes/lib_remote.php"); +require_once("modules/config_games/server_config_parser.php"); +require_once("modules/steam_workshop/functions.php"); require_once('includes/form_table_class.php'); +echo ''."\n". + ''."\n". + ''; function exec_ogp_module() { - global $db, $view, $settings; + + Global $db,$view,$settings; echo '

Steam Workshop

'; - - if (!isset($_GET['home_id-mod_id-ip-port']) || $_GET['home_id-mod_id-ip-port'] === '') { - print_failure(sw_text('no_game_servers_assigned')); + define('CONFIGS', "modules/steam_workshop/game_configs/"); + + if(isset($_GET['home_id-mod_id-ip-port']) && $_GET['home_id-mod_id-ip-port'] != "") + list($home_id, $mod_id, $ip, $port) = explode("-", $_GET['home_id-mod_id-ip-port']); + else + { + print_failure(get_lang('no_game_servers_assigned')); return; } - - list($homeId, $modId) = explode('-', $_GET['home_id-mod_id-ip-port']); - - $isAdmin = $db->isAdmin($_SESSION['user_id']); - $homeCfg = $isAdmin ? $db->getGameHome($homeId) : $db->getUserGameHome($_SESSION['user_id'], $homeId); - if (!$homeCfg) { - print_failure(sw_text('game_home_not_found')); - return; + + if(!isset($_POST['workshop_mod_id']) and !isset($_GET['show_log']) and !isset($_POST['manual_workshop_mod_id'])) + { + echo ""; } - - $serverXml = read_server_config(SERVER_CONFIG_LOCATION . '/' . $homeCfg['home_cfg_file']); - if ($serverXml === false) { - print_failure(sw_text('failed_reading_xml_file', SERVER_CONFIG_LOCATION . '/' . $homeCfg['home_cfg_file'])); - return; - } - - if (!isset($homeCfg['mods'][$modId]['mod_key'])) { - print_failure(sw_text('mod_id_does_not_exists_in_home', $modId, $homeId)); - return; - } - - $modKey = $homeCfg['mods'][$modId]['mod_key']; - $modXml = xml_get_mod($serverXml, $modKey); - if (!$modXml) { - print_failure(sw_text('mod_key_not_found_from_xml', $modKey)); - return; - } - - $configStore = new WorkshopConfigStore('modules/steam_workshop/data/configs.json'); - $stateStore = new WorkshopStateStore('modules/steam_workshop/data'); - $resolver = new WorkshopResolver(); - - $config = $configStore->get($homeId); - if ($config === null) { - $config = workshop_build_default_config($homeCfg, $modXml, $settings); - $configStore->put($homeId, $config); - } - - $remote = new OGPRemoteLibrary($homeCfg['agent_ip'], $homeCfg['agent_port'], $homeCfg['encryption_key'], $homeCfg['timeout']); - if ($remote->status_chk() !== 1) { - print_failure(sw_text('remote_server_offline')); - return; - } - - $syncService = new WorkshopSyncService($remote, $homeCfg, $configStore, $stateStore, $resolver); - $state = $stateStore->get($homeId); - $items = $resolver->resolveItems($config); - $details = $resolver->fetchItemDetails($items); - - $message = ''; - if (isset($_POST['action'])) { - switch ($_POST['action']) { - case 'add_item': - if (isset($_POST['item_id']) && preg_match('/^[0-9]+$/', $_POST['item_id'])) { - $config['workshop_item_ids'][] = $_POST['item_id']; - $config['workshop_item_ids'] = array_values(array_unique(array_filter($config['workshop_item_ids'], 'strlen'))); - $configStore->put($homeId, $config); - $items = $resolver->resolveItems($config); - $details = $resolver->fetchItemDetails($items); - $message = sw_text('mod_installation_started'); - } else { - print_failure(sw_text('invalid_mod_id')); + + $isAdmin = $db->isAdmin( $_SESSION['user_id'] ); + + if($isAdmin) + $home_cfg = $db->getGameHome($home_id); + else + $home_cfg = $db->getUserGameHome($_SESSION['user_id'],$home_id); + + if($home_cfg) + { + $server_xml = read_server_config(SERVER_CONFIG_LOCATION."/".$home_cfg['home_cfg_file']); + if($server_xml === FALSE) + { + print_failure(get_lang_f('failed_reading_xml_file', SERVER_CONFIG_LOCATION."/".$home_cfg['home_cfg_file'])); + return; + } + + if(!isset($home_cfg['mods'][$mod_id]['mod_key'])) + { + print_failure(get_lang_f('mod_id_does_not_exists_in_home', $mod_id, $home_id)); + return; + } + + $modkey = $home_cfg['mods'][$mod_id]['mod_key']; + $mod_xml = xml_get_mod($server_xml, $modkey); + + if (!$mod_xml) + { + print_failure(get_lang_f('mod_key_not_found_from_xml', $modkey)); + return; + } + + if(preg_match('/(linux|win)(32|64)?/i', $home_cfg['game_key'], $matches)) + { + $os = ""; + if(strtolower($matches[1]) == 'linux') + $os = "Linux"; + elseif(strtolower($matches[1]) == 'win') + $os = "Windows"; + } + else + { + print_failure(get_lang_f('unable_to_get_os_from_game_key', $home_cfg['game_key'])); + return; + } + + $xml_file = CONFIGS.$mod_xml->installer_name."_".$os.".xml"; + + if(!file_exists($xml_file)) + { + print_failure(get_lang('workshop_configuration_not_found')); + return; + } + + $dom = new DOMDocument(); + + if ( @$dom->load($xml_file) === FALSE ) + { + print_failure(get_lang('workshop_configuration_file_has_bad_format')); + return; + } + + $xml = simplexml_load_file($xml_file); + + if($xml !== false) + { + $remote = new OGPRemoteLibrary($home_cfg['agent_ip'],$home_cfg['agent_port'],$home_cfg['encryption_key'], $home_cfg['timeout']); + + if($remote->status_chk() !== 1) + { + print_failure(get_lang('remote_server_offline')); + } + + if(isset($_GET['show_log'])) + { + $update_active = $remote->get_log(OGP_SCREEN_TYPE_UPDATE,$home_id,clean_path($home_cfg['home_path']),$log_txt); + if ( $update_active == 1 ) + { + if(isset($_POST['sgc'])) + { + $remote->send_steam_guard_code($home_id, $_POST['sgc']); + return; + } + echo "

". get_lang("update_in_progress") ."

\n"; + echo "
".$log_txt."
\n\n
\n"; + if(preg_match('/Two-factor code:$/m', $log_txt) and !isset($_GET['get_sgc'])) + { + $view->refresh("?m=steam_workshop&p=main&home_id-mod_id-ip-port=".$_GET['home_id-mod_id-ip-port']."&get_sgc=show&show_log",0); + return; + } + if(isset($_GET['get_sgc']) && $_GET['get_sgc'] == 'show') + return; + + echo "

"; + echo get_lang("refresh_steam_workshop_status") ."

"; + $view->refresh("?m=steam_workshop&p=main&home_id-mod_id-ip-port=".$_GET['home_id-mod_id-ip-port']."&show_log",5); } - break; - case 'add_collection': - if (isset($_POST['collection_id']) && preg_match('/^[0-9]+$/', $_POST['collection_id'])) { - $config['collection_ids'][] = $_POST['collection_id']; - $config['collection_ids'] = array_values(array_unique(array_filter($config['collection_ids'], 'strlen'))); - $configStore->put($homeId, $config); - $items = $resolver->resolveItems($config); - $details = $resolver->fetchItemDetails($items); - $message = sw_text('settings_updated'); - } else { - print_failure(sw_text('invalid_mod_id')); + else + { + print_success( get_lang("update_completed") ); + echo "
".$log_txt."
\n"; + echo "
<< ". get_lang("back") ."
"; } - break; - case 'remove_item': - if (isset($_POST['item_id'])) { - $config['workshop_item_ids'] = array_values(array_filter($config['workshop_item_ids'], function ($id) { - return isset($_POST['item_id']) ? ($id !== $_POST['item_id']) : true; - })); - $configStore->put($homeId, $config); - $items = $resolver->resolveItems($config); - $details = $resolver->fetchItemDetails($items); - $message = sw_text('settings_updated'); + } + else + { + if(isset($_POST['workshop_mod_id']) OR isset($_POST['manual_workshop_mod_id'])) + { + $failure = false; + if(isset($_POST['manual_workshop_mod_id']) and $_POST['manual_workshop_mod_id'] != "" and preg_match('/^([0-9]+,?)+$/', $_POST['manual_workshop_mod_id'])) + { + $mods_list = $_POST['manual_workshop_mod_id']; + $mod_id_array = explode(',', $mods_list); + foreach($mod_id_array as $workshop_mod_id) + { + $exist = false; + foreach($xml->mods->mod as $mod) + { + if($mod['id'] == $workshop_mod_id) + { + $exist = true; + break; + } + } + + if(belongs_to_workshop($workshop_mod_id, $xml->workshop_id)) + { + if(!$exist) + { + list($mod_title, $mod_description, $mod_image_url, $download_url, $filename, $file_size) = get_mod_info($workshop_mod_id); + //add mods to the xml + $mod = new SimpleXMLElement(''); + $mod->addAttribute('id', $workshop_mod_id); + $mod->addChild('name', $mod_title); + $mod->addChild('description', base64_encode($mod_description)); + $mod->addChild('image_url', $mod_image_url); + $mod->addChild('download_url', $download_url); + $mod->addChild('filename', $filename); + $mod->addChild('file_size', $file_size); + $moddom = dom_import_simplexml($mod)->ownerDocument; + $moddom->formatOutput = true; + $mod_string = $moddom->saveXML($moddom->documentElement); + + $dom = dom_import_simplexml($xml)->ownerDocument; + $dom->formatOutput = true; + + $mods = $dom->getElementsByTagName('mods')->item(0); + + $f = $dom->createDocumentFragment(); + $f->appendXML($mod_string."\n"); + $mods->appendChild($f); + + + file_put_contents($xml_file, $dom->saveXML()); + $xml = simplexml_load_file($xml_file); + } + } + else + { + print_failure(get_lang_f('mod_does_not_belong_to_workshop', $workshop_mod_id)); + $failure = true; + } + } + } + elseif(isset($_POST['workshop_mod_id'])) + { + $mods_list = implode(',',$_POST['workshop_mod_id']); + } + + if(isset($_POST['install']) and !$failure and isset($mods_list) and preg_match('/^([0-9]+,?)+$/', $mods_list)) + { + $config = $xml->config; + $anonymous_login = $xml->anonymous_login; + $download_method = $xml->download_method; + $user = $settings['steam_user']; + $pass = $settings['steam_pass']; + $regex = $config->regex; + $mods_backreference_index = (int)$config->mods_backreference_index; + $variable = $config->variable; + $place_after = $config->place_after; + $mod_string = $config->mod_string; + $string_separator = $config->string_separator; + $config_file_path = clean_path($home_cfg['home_path']."/".$config->filepath); + $post_install = $xml->post_install; + $mod_names_list = get_mod_names_list($mods_list, $xml->mods->mod); + $mods_full_path = clean_path($home_cfg['home_path'].'/'.$xml->mods_path); + $workshop_id = $xml->workshop_id; + + $url_list = ""; + $filename_list = ""; + if($download_method == "steamapi") + { + foreach(explode(',', $mods_list) as $workshop_mod_id) + { + foreach($xml->mods->mod as $mod) + { + if($mod['id'] == $workshop_mod_id) + { + $separator = $url_list == ""?"":","; + $url_list .= $separator.$mod->download_url; + $filename_list .= $separator.$mod->filename; + } + } + } + } + + $steam_out = $remote->steam_workshop($home_id, $mods_full_path, + $workshop_id, $mods_list, + $regex, $mods_backreference_index, + $variable, $place_after, $mod_string, + $string_separator, $config_file_path, + $post_install, $mod_names_list, + $anonymous_login, $user, $pass, + $download_method, $url_list, $filename_list); + if ( $steam_out === 1 ) + { + print_success( get_lang("mod_installation_started") ); + $view->refresh("?m=steam_workshop&p=main&home_id-mod_id-ip-port=".$_GET['home_id-mod_id-ip-port']."&show_log", 2); + } + elseif( $steam_out === 0 ) + { + print_failure( get_lang("failed_to_start_steam_workshop") ); + return; + } + elseif ( $steam_out === -1 ) + { + print_failure( get_lang("connection_error") ); + } + } + + if(isset($_POST['show_info']) and !$failure and isset($mods_list) and preg_match('/^([0-9]+,?)+$/', $mods_list)) + { + $mod_id_array = explode(',', $mods_list); + echo ""; + foreach($xml->mods->mod as $mod) + { + if(in_array($mod['id'],$mod_id_array)) + { + echo ""; + } + } + echo "

".$mod->name."

". + "
". + "
".htmlentities(base64_decode($mod->description))."
".get_lang('back').""; + } } - break; - case 'sync_now': - $result = $syncService->sync($homeId); - if ($result->success) { - print_success($result->message); - } else { - print_failure($result->message); + else + { + $ft = new FormTable(); + $ft->start_form("?m=steam_workshop&p=main&home_id-mod_id-ip-port=".$_GET['home_id-mod_id-ip-port'], "post", "onsubmit='return isValidForm(this)' data-form-error='".get_lang('select_at_least_one_mod_or_enter_mod_id')."'"); + $ft->start_table(); + if(count($xml->mods->mod) > 0) + { + echo '
'; + foreach($xml->mods->mod as $mod) + echo "
"; + echo '
'; + } + $ft->add_field('string', 'manual_workshop_mod_id',''); + $ft->end_table(); + $ft->add_button("submit", "install", get_lang('install_mod')); + $ft->add_button("submit", "show_info", get_lang('show_mod_info')); + $ft->end_form(); } - $state = $stateStore->get($homeId); - break; + } + } + else + { + print_failure(get_lang('workshop_configuration_file_has_bad_format')); + return; } } - - if ($message !== '') { - print_success($message); + else + { + print_failure(get_lang('game_home_not_found')); + return; } - - echo '
'; - echo '

' . sw_text('installed_mods') . '

'; - if (empty($items)) { - echo '

' . sw_text('no_mods_found') . '

'; - } else { - echo ''; - echo ''; - foreach ($items as $id) { - $name = isset($details[$id]['title']) ? htmlentities($details[$id]['title']) : $id; - $status = isset($state['items'][$id]) ? sw_text('installed') : sw_text('pending'); - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
ID' . sw_text('name') . '' . sw_text('status') . '
' . $id . '' . $name . '' . $status . ''; - echo '
'; - echo ''; - echo ''; - echo '
'; - echo '
'; - } - echo '
'; - - echo '
'; - echo '

' . sw_text('add_mod') . '

'; - echo '
'; - echo ' '; - echo ''; - echo '
'; - echo '
'; - echo ' '; - echo ''; - echo '
'; - echo '
'; - - echo '
'; - echo '

' . sw_text('actions') . '

'; - echo '
'; - echo ''; - echo '
'; - echo '
'; } ?> diff --git a/modules/steam_workshop/uninstall.php b/modules/steam_workshop/uninstall.php index fb769b66..f586ec4d 100644 --- a/modules/steam_workshop/uninstall.php +++ b/modules/steam_workshop/uninstall.php @@ -36,15 +36,15 @@ function exec_ogp_module() list($home_id, $mod_id, $ip, $port) = explode("-", $_GET['home_id-mod_id-ip-port']); else { - print_failure(sw_text('no_game_servers_assigned')); + print_failure(get_lang('no_game_servers_assigned')); return; } if(!isset($_POST['uninstall'])) { echo ""; } @@ -60,13 +60,13 @@ function exec_ogp_module() $server_xml = read_server_config(SERVER_CONFIG_LOCATION."/".$home_cfg['home_cfg_file']); if($server_xml === FALSE) { - print_failure(sw_text('failed_reading_xml_file', SERVER_CONFIG_LOCATION."/".$home_cfg['home_cfg_file'])); + print_failure(get_lang_f('failed_reading_xml_file', SERVER_CONFIG_LOCATION."/".$home_cfg['home_cfg_file'])); return; } if(!isset($home_cfg['mods'][$mod_id]['mod_key'])) { - print_failure(sw_text('mod_id_does_not_exists_in_home', $mod_id, $home_id)); + print_failure(get_lang_f('mod_id_does_not_exists_in_home', $mod_id, $home_id)); return; } @@ -75,7 +75,7 @@ function exec_ogp_module() if (!$mod_xml) { - print_failure(sw_text('mod_key_not_found_from_xml', $modkey)); + print_failure(get_lang_f('mod_key_not_found_from_xml', $modkey)); return; } @@ -83,7 +83,7 @@ function exec_ogp_module() if(!isset($matches[1])) { - print_failure(sw_text('unable_to_get_os_from_game_key', $home_cfg['game_key'])); + print_failure(get_lang_f('unable_to_get_os_from_game_key', $home_cfg['game_key'])); return; } @@ -94,7 +94,7 @@ function exec_ogp_module() if(!isset($os)) { - print_failure(sw_text('unable_to_get_os_from_game_key', $home_cfg['game_key'])); + print_failure(get_lang_f('unable_to_get_os_from_game_key', $home_cfg['game_key'])); return; } @@ -102,7 +102,7 @@ function exec_ogp_module() if(!file_exists($xml_file)) { - print_failure(sw_text('no_workshop_configuration_available_for_this_game')); + print_failure(get_lang('no_workshop_configuration_available_for_this_game')); return; } @@ -110,7 +110,7 @@ function exec_ogp_module() if ( @$dom->load($xml_file) === FALSE ) { - print_failure(sw_text('workshop_configuration_file_has_bad_format')); + print_failure(get_lang('workshop_configuration_file_has_bad_format')); return; } @@ -122,7 +122,7 @@ function exec_ogp_module() if($remote->status_chk() !== 1) { - print_failure(sw_text('remote_server_offline')); + print_failure(get_lang('remote_server_offline')); } if(isset($_POST['uninstall']) and isset($_POST['mod_string'])) @@ -134,10 +134,10 @@ function exec_ogp_module() if($result !== FALSE) $output .= $result."\n"; else - $output .= sw_text('failed_uninstalling_mod', $mod_string)."\n"; + $output .= get_lang_f('failed_uninstalling_mod', $mod_string)."\n"; } echo "
$output
"; - echo "".sw_text('back').""; + echo "".get_lang('back').""; } else { @@ -153,25 +153,25 @@ function exec_ogp_module() echo "
"; echo ''; $ft->end_table(); - $ft->add_button("submit", "uninstall", sw_text('uninstall_mods')); + $ft->add_button("submit", "uninstall", get_lang('uninstall_mods')); $ft->end_form(); } else { - print_failure(sw_text('there_are_no_mods_installed_on_this_game_server')); + print_failure(get_lang('there_are_no_mods_installed_on_this_game_server')); return; } } } else { - print_failure(sw_text('workshop_configuration_file_has_bad_format')); + print_failure(get_lang('workshop_configuration_file_has_bad_format')); return; } } else { - print_failure(sw_text('game_home_not_found')); + print_failure(get_lang('game_home_not_found')); return; } } diff --git a/modules/steam_workshop/workshop_admin.php b/modules/steam_workshop/workshop_admin.php index 5cfcb63c..7e808168 100644 --- a/modules/steam_workshop/workshop_admin.php +++ b/modules/steam_workshop/workshop_admin.php @@ -22,104 +22,234 @@ * */ require_once('includes/form_table_class.php'); -require_once('modules/config_games/server_config_parser.php'); -require_once('modules/steam_workshop/functions.php'); -require_once('modules/steam_workshop/lib/Workshop.php'); - +require_once("modules/steam_workshop/functions.php"); function exec_ogp_module() { - global $db; - echo '

Steam Workshop – Admin

'; - - $homes = $db->getGameHomes(); - $eligibleHomes = []; - foreach ($homes as $home) { - $serverXml = read_server_config(SERVER_CONFIG_LOCATION . '/' . $home['home_cfg_file']); - if ($serverXml !== false && isset($serverXml->installer) && strtolower((string)$serverXml->installer) === 'steamcmd') { - $eligibleHomes[$home['home_id']] = $home; + + Global $db,$view; + echo '

Steam Workshop

'; + define('CONFIGS', "modules/steam_workshop/game_configs/"); + + if(isset($_REQUEST['home_cfg_id-mod_cfg_id-os'])) + list($home_cfg_id, $mod_cfg_id, $os) = explode('-', $_REQUEST['home_cfg_id-mod_cfg_id-os']); + + if(isset($home_cfg_id) and isset($mod_cfg_id)) + { + $gameCfg = $db->getGameCfg($home_cfg_id); + $cfgMods = $db->getCfgMods($home_cfg_id); + + foreach($cfgMods as $cfgMod) + { + if($cfgMod['mod_cfg_id'] == $mod_cfg_id) + $modkey = $cfgMod['mod_key']; + } + + $server_xml = read_server_config(SERVER_CONFIG_LOCATION."/".$gameCfg['home_cfg_file']); + + $mod_xml = xml_get_mod($server_xml, $modkey); + + if (!$mod_xml) + { + print_failure(get_lang_f('mod_key_not_found_from_xml',$modkey)); + return; + } + $xml_file = CONFIGS.$mod_xml->installer_name."_".$os.".xml"; + + if(isset($_POST['save_config'])) + { + $xml = new SimpleXMLElement(''); + $xml->addChild('workshop_id', $_POST['workshop_id']); + $xml->addChild('download_method', $_POST['download_method']); + $xml->addChild('anonymous_login', $_POST['anonymous_login']); + $xml->addChild('mods_path', $_POST['mods_path']); + $mods = $xml->addChild('mods'); + if(file_exists($xml_file)) + { + $file_xml = simplexml_load_file($xml_file); + foreach($file_xml->mods->mod as $xml_mod) + { + $mod = $mods->addChild('mod'); + $mod->addAttribute('id', $xml_mod['id']); + $mod->addChild('name', $xml_mod->name); + $mod->addChild('description', $xml_mod->description); + $mod->addChild('image_url', $xml_mod->image_url); + $mod->addChild('download_url', $xml_mod->download_url); + $mod->addChild('filename', $xml_mod->filename); + $mod->addChild('file_size', $xml_mod->file_size); + } + } + + $config = $xml->addChild('config'); + $config->addChild('regex', $_POST['regex']); + $config->addChild('mods_backreference_index', $_POST['mods_backreference_index']); + $config->addChild('variable', $_POST['variable']); + $config->addChild('place_after', $_POST['place_after']); + $config->addChild('mod_string', $_POST['mod_string']); + $config->addChild('string_separator', $_POST['string_separator']); + $config->addChild('filepath', $_POST['filepath']); + $xml->addChild('post_install', str_replace('&','&',$_POST['post_install'])); + $xml->addChild('uninstall', str_replace('&','&',$_POST['uninstall'])); + + $dom = dom_import_simplexml($xml)->ownerDocument; + $dom->formatOutput = true; + file_put_contents($xml_file, $dom->saveXML()); + } + + if(isset($_POST['remove_mods'])) + { + $xml = new SimpleXMLElement(''); + $xml->addChild('workshop_id', $_POST['workshop_id']); + $xml->addChild('download_method', $_POST['download_method']); + $xml->addChild('anonymous_login', $_POST['anonymous_login']); + $xml->addChild('mods_path', $_POST['mods_path']); + $mods = $xml->addChild('mods'); + if(file_exists($xml_file)) + { + $file_xml = simplexml_load_file($xml_file); + foreach($file_xml->mods->mod as $xml_mod) + { + if(in_array($xml_mod['id'],$_POST['workshop_mod_id'])) + continue; + $mod = $mods->addChild('mod'); + $mod->addAttribute('id', $xml_mod['id']); + $mod->addChild('name', $xml_mod->name); + $mod->addChild('description', $xml_mod->description); + $mod->addChild('image_url', $xml_mod->image_url); + $mod->addChild('download_url', $xml_mod->download_url); + $mod->addChild('filename', $xml_mod->filename); + $mod->addChild('file_size', $xml_mod->file_size); + } + } + $config = $xml->addChild('config'); + $config->addChild('regex', $_POST['regex']); + $config->addChild('mods_backreference_index', $_POST['mods_backreference_index']); + $config->addChild('variable', $_POST['variable']); + $config->addChild('place_after', $_POST['place_after']); + $config->addChild('mod_string', $_POST['mod_string']); + $config->addChild('string_separator', $_POST['string_separator']); + $config->addChild('filepath', $_POST['filepath']); + $xml->addChild('post_install', $_POST['post_install']); + $xml->addChild('uninstall', $_POST['uninstall']); + + $dom = dom_import_simplexml($xml)->ownerDocument; + $dom->formatOutput = true; + file_put_contents($xml_file, $dom->saveXML()); + } + + if(file_exists($xml_file)) + { + $xml = simplexml_load_file($xml_file); + + $workshop_id = $xml->workshop_id; + $download_method = $xml->download_method; + $anonymous_login = $xml->anonymous_login; + $mods_path = $xml->mods_path; + $regex = $xml->config->regex; + $mods_backreference_index = $xml->config->mods_backreference_index; + $variable = $xml->config->variable; + $place_after = $xml->config->place_after; + $mod_string = $xml->config->mod_string; + $string_separator = $xml->config->string_separator; + $filepath = $xml->config->filepath; + $post_install = $xml->post_install; + $uninstall = $xml->uninstall; + } + + + } + + $game_cfgs = $db->getGameCfgs(); + $games[0] = get_lang('select_game'); + foreach($game_cfgs as $game_cfg) + { + $server_xml = read_server_config(SERVER_CONFIG_LOCATION."/".$game_cfg['home_cfg_file']); + if(isset($server_xml->installer) and $server_xml->installer == "steamcmd") + { + $cfgMods = $db->getCfgMods($game_cfg['home_cfg_id']); + foreach($cfgMods as $cfgMod) + { + $mod_xml = xml_get_mod($server_xml, $cfgMod['mod_key']); + if(isset($mod_xml->installer_name) and !in_array((string)$mod_xml->installer_name, get_blacklist())) + { + preg_match('/(linux|win)(32|64)?/i', $game_cfg['game_key'], $matches); + + if(strtolower($matches[1]) == 'linux') + $os = "Linux"; + elseif(strtolower($matches[1]) == 'win') + $os = "Windows"; + if(isset($matches[2]) and strtolower($matches[2]) == '64') + $arch = "64"; + else + $arch = "32"; + + $modname = strtolower($cfgMod['mod_name']) == "none"? "":" [MOD:" . $cfgMod['mod_name']."]"; + + $games[$game_cfg['home_cfg_id'].'-'.$cfgMod['mod_cfg_id'].'-'.$os] = $game_cfg['game_name'] . " (" . $os . " " . $arch . "bits)$modname"; + } + } } } - - if (empty($eligibleHomes)) { - print_failure(sw_text('no_game_servers_assigned')); - return; - } - - $selectedHomeId = isset($_REQUEST['home_id']) ? $_REQUEST['home_id'] : array_key_first($eligibleHomes); - if (!isset($eligibleHomes[$selectedHomeId])) { - $selectedHomeId = array_key_first($eligibleHomes); - } - - $homeCfg = $db->getGameHome($selectedHomeId); - $serverXml = read_server_config(SERVER_CONFIG_LOCATION . '/' . $homeCfg['home_cfg_file']); - $mods = isset($homeCfg['mods']) ? $homeCfg['mods'] : []; - $firstMod = reset($mods); - $modKey = isset($firstMod['mod_key']) ? $firstMod['mod_key'] : null; - $modXml = $modKey ? xml_get_mod($serverXml, $modKey) : null; - - $configStore = new WorkshopConfigStore('modules/steam_workshop/data/configs.json'); - $config = $configStore->get($selectedHomeId); - if ($config === null) { - $config = workshop_build_default_config($homeCfg, $modXml, []); - $configStore->put($selectedHomeId, $config); - } - - if (isset($_POST['save_config'])) { - $config['workshop_app_id'] = trim($_POST['workshop_app_id']); - $config['download_method'] = $_POST['download_method']; - $config['deploy_mode'] = $_POST['deploy_mode']; - $config['deploy_destination'] = clean_path($_POST['deploy_destination']); - $config['staging_path'] = clean_path($_POST['staging_path']); - $config['regex'] = $_POST['regex']; - $config['mods_backreference_index'] = (int)$_POST['mods_backreference_index']; - $config['variable'] = $_POST['variable']; - $config['place_after'] = $_POST['place_after']; - $config['mod_string'] = $_POST['mod_string']; - $config['string_separator'] = $_POST['string_separator']; - $config['filepath'] = clean_path($_POST['filepath']); - $config['anonymous_login'] = isset($_POST['anonymous_login']) && $_POST['anonymous_login'] === '1'; - $config['steam_username'] = $_POST['steam_username']; - $config['steam_password'] = $_POST['steam_password']; - $config['check_on_start'] = isset($_POST['check_on_start']) && $_POST['check_on_start'] === '1'; - $config['periodic_check_minutes'] = $_POST['periodic_check_minutes'] === '' ? null : (int)$_POST['periodic_check_minutes']; - $config['apply_updates'] = $_POST['apply_updates']; - $configStore->put($selectedHomeId, $config); - print_success(sw_text('settings_updated')); - } - - $downloadMethods = array('steamcmd' => 'steamcmd'); - $deployModes = array('copy' => 'copy', 'symlink' => 'symlink'); - $applyModes = array('on_start_only' => 'on_start_only', 'download_now_apply_on_next_restart' => 'download_now_apply_on_next_restart'); - - $homeOptions = []; - foreach ($eligibleHomes as $id => $home) { - $homeOptions[$id] = $home['home_name']; - } - + + $download_methods = array("steamcmd", "steamapi"); + $ft = new FormTable(); - $ft->start_form('?m=steam_workshop&p=workshop_admin', 'post', 'autocomplete="off"'); + $ft->start_form("?m=steam_workshop&p=workshop_admin", "post", "autocomplete=\"off\""); $ft->start_table(); - $ft->add_custom_field('home_id', create_drop_box_from_array_onchange($homeOptions, 'home_id', $selectedHomeId)); - $ft->add_field('string', 'workshop_app_id', $config['workshop_app_id']); - $ft->add_custom_field('download_method', create_drop_box_from_array($downloadMethods, 'download_method', $config['download_method'])); - $ft->add_custom_field('deploy_mode', create_drop_box_from_array($deployModes, 'deploy_mode', $config['deploy_mode'])); - $ft->add_field('string', 'deploy_destination', $config['deploy_destination']); - $ft->add_field('string', 'staging_path', $config['staging_path']); - $ft->add_field('string', 'regex', $config['regex']); - $ft->add_field('string', 'mods_backreference_index', $config['mods_backreference_index']); - $ft->add_field('string', 'variable', $config['variable']); - $ft->add_field('string', 'place_after', $config['place_after']); - $ft->add_field('string', 'mod_string', $config['mod_string']); - $ft->add_field('string', 'string_separator', $config['string_separator']); - $ft->add_field('string', 'filepath', $config['filepath']); - $ft->add_field('on_off', 'anonymous_login', $config['anonymous_login'] ? '1' : '0'); - $ft->add_field('string', 'steam_username', $config['steam_username']); - $ft->add_field('password', 'steam_password', $config['steam_password']); - $ft->add_field('on_off', 'check_on_start', $config['check_on_start'] ? '1' : '0'); - $ft->add_field('string', 'periodic_check_minutes', $config['periodic_check_minutes']); - $ft->add_custom_field('apply_updates', create_drop_box_from_array($applyModes, 'apply_updates', $config['apply_updates'])); - $ft->end_table(); - $ft->add_button('submit', 'save_config', sw_text('save_config')); - $ft->end_form(); + $ft->add_custom_field('game', create_drop_box_from_array_onchange($games, "home_cfg_id-mod_cfg_id-os", @$_REQUEST['home_cfg_id-mod_cfg_id-os'])); + if(isset($home_cfg_id) and isset($mod_cfg_id)) + { + $ft->add_field('string','workshop_id',@$workshop_id); + $ft->add_custom_field('download_method',create_drop_box_from_array($download_methods, "download_method", @$download_method)); + $ft->add_field('on_off','anonymous_login',@$anonymous_login); + $ft->add_field('string','mods_path',@$mods_path); + $ft->add_field('string','regex',@$regex); + $ft->add_field('string','mods_backreference_index',@$mods_backreference_index); + $ft->add_field('string','variable',@$variable); + $ft->add_field('string','place_after',@$place_after); + $ft->add_field('string','mod_string',@$mod_string); + $ft->add_field('string','string_separator',@$string_separator); + $ft->add_field('string','filepath',@$filepath); + $ft->add_field('text','post_install',@$post_install); + $ft->add_field('text','uninstall',@$uninstall); + + + $ft->end_table(); + $ft->add_button("submit","save_config",get_lang('save_config')); + $ft->end_form(); + } + else + { + $ft->end_table(); + $ft->end_form(); + } + + if(isset($xml) and count($xml->mods->mod) > 0) + { + $ft = new FormTable(); + $ft->start_form("?m=steam_workshop&p=workshop_admin&home_cfg_id-mod_cfg_id-os=".$_REQUEST['home_cfg_id-mod_cfg_id-os'], "post", "autocomplete=\"off\""); + $ft->start_table(); + echo ""; + $ft->add_field_hidden('workshop_id',$workshop_id); + $ft->add_field_hidden('download_method',$download_method); + $ft->add_field_hidden('anonymous_login',$anonymous_login); + $ft->add_field_hidden('mods_path',$mods_path); + $ft->add_field_hidden('regex',$regex); + $ft->add_field_hidden('mods_backreference_index',$mods_backreference_index); + $ft->add_field_hidden('variable',$variable); + $ft->add_field_hidden('place_after',$place_after); + $ft->add_field_hidden('mod_string',$mod_string); + $ft->add_field_hidden('string_separator',$string_separator); + $ft->add_field_hidden('filepath',$filepath); + $ft->add_field_hidden('post_install',$post_install); + $ft->add_field_hidden('uninstall',$uninstall); + echo "". + '
'; + foreach($xml->mods->mod as $mod) + echo "
"; + echo '
'; + $ft->end_table(); + $ft->add_button("submit","remove_mods",get_lang('remove_mods')); + $ft->end_form(); + } } ?> \ No newline at end of file