30 'Apache' => [
'type' =>
'binary'],
31 'Bruno' => [
'type' =>
'tools'],
32 'Composer' => [
'type' =>
'tools'],
33 'Ghostscript' => [
'type' =>
'tools'],
34 'Git' => [
'type' =>
'tools'],
35 'Mailpit' => [
'type' =>
'binary'],
36 'MariaDB' => [
'type' =>
'binary'],
37 'Memcached' => [
'type' =>
'binary'],
38 'MySQL' => [
'type' =>
'binary'],
39 'Ngrok' => [
'type' =>
'tools'],
40 'NodeJS' => [
'type' =>
'binary'],
41 'Perl' => [
'type' =>
'tools'],
42 'PHP' => [
'type' =>
'binary'],
43 'PhpMyAdmin' => [
'type' =>
'application'],
44 'PhpPgAdmin' => [
'type' =>
'application'],
45 'PostgreSQL' => [
'type' =>
'binary'],
46 'PowerShell' => [
'type' =>
'tools'],
47 'Python' => [
'type' =>
'tools'],
48 'Ruby' => [
'type' =>
'tools'],
49 'Xlight' => [
'type' =>
'binary']
86 if ($isPrerelease && $includePr == 1) {
87 return '<span class="text-danger">' . htmlspecialchars($version) .
' PR</span>';
90 return htmlspecialchars($version);
103 $moduleName = str_replace(
'module-',
'', $moduleName);
107 foreach ($this->modules as $key => $moduleInfo) {
108 if (strtolower($key) === strtolower($moduleName)) {
123 return array_keys( $this->modules );
141 if (!$validation[
'valid']) {
182 $remoteFileCreationTime = strtotime(isset($headers[
'Date']) ? $headers[
'Date'] :
'');
196 if (!file_exists($this->jsonFilePath)) {
201 return filectime($this->jsonFilePath);
213 if ($headers ===
false || !isset($headers[
'Date'])) {
229 Log::debug(
'Headers: ' . print_r($headers,
true));
240 $content = @file_get_contents( $this->jsonFilePath );
241 if ( $content ===
false ) {
242 Log::error(
'Error fetching content from JSON file: ' . $this->jsonFilePath );
244 return [
'error' =>
'Error fetching JSON file'];
247 $data = json_decode( $content,
true );
248 if ( json_last_error() !== JSON_ERROR_NONE ) {
249 Log::error(
'Error decoding JSON content: ' . json_last_error_msg() );
251 return [
'error' =>
'Error decoding JSON content'];
265 Log::debug(
'Fetching JSON file: ' . $this->jsonFilePath );
270 if ( $jsonContent ===
false ) {
272 throw new Exception(
'Failed to fetch JSON content from the URL.' );
276 $result = file_put_contents( $this->jsonFilePath, $jsonContent );
280 throw new Exception(
'Failed to save JSON content to the specified path.' );
284 return [
'success' =>
'JSON content fetched and saved successfully'];
303 foreach ( $jsonData as $entry ) {
304 if ( is_array( $entry ) ) {
305 if ( isset( $entry[
'module'] ) && is_string( $entry[
'module'] ) ) {
306 if ( isset( $entry[
'versions'] ) && is_array( $entry[
'versions'] ) ) {
307 $versions[$entry[
'module']] = array_column( $entry[
'versions'],
null,
'version' );
312 Log::error(
'Invalid entry format in JSON data' );
319 return [
'error' =>
'No versions found'];
343 Log::debug(
'getModuleUrl called for module: ' . $module .
' version: ' . $version );
344 $url = trim( $this->versions[
'module-' . strtolower( $module )][$version][
'url'] );
346 Log::debug(
'Found URL for version: ' . $version .
' URL: ' . $url );
351 Log::error(
'Version not found: ' . $version );
353 return [
'error' =>
'Version not found'];
376 Log::debug(
'checkDownloadId method called.' );
380 Log::error(
'Global configuration is not set.' );
386 Log::debug(
'DownloadId is: ' . $DownloadId );
389 if ( empty( $DownloadId ) ) {
405 Log::error(
'Failed to validate QuickPick license - API server unavailable' );
414 if ( json_last_error() !== JSON_ERROR_NONE ) {
415 Log::error(
'Error decoding JSON response: ' . json_last_error_msg() );
421 if ( isset( $data[
'success'] ) && $data[
'success'] ===
true && isset( $data[
'data'] ) && is_array( $data[
'data'] ) && count( $data[
'data'] ) > 0 ) {
422 Log::debug(
'License key valid: ' . $DownloadId );
427 Log::error(
'Invalid license key: ' . $DownloadId );
451 if ( is_array( $moduleUrl ) && isset( $moduleUrl[
'error'] ) ) {
452 Log::error(
'Module URL not found for module: ' . $module .
' version: ' . $version );
454 return [
'error' =>
'Module URL not found'];
457 if ( empty( $moduleUrl ) ) {
458 Log::error(
'Module URL not found for module: ' . $module .
' version: ' . $version );
460 return [
'error' =>
'Module URL not found'];
472 Log::debug(
'Enhanced mode: ' . ($enhancedMode ?
'enabled' :
'disabled'));
475 if (isset(
$response[
'success']) && $enhancedMode == 1) {
477 Log::debug(
'Enhanced mode enabled - Updating config for module: ' . $module .
' version: ' . $version);
480 if ($configUpdated) {
487 Log::debug(
'Config updated successfully, launching reload to apply changes...');
490 if (ob_get_level() > 0) {
493 echo json_encode([
'phase' =>
'updating',
'message' =>
'Applying version changes...']) . PHP_EOL;
497 Log::debug(
'Clearing caches before reload...');
505 Log::debug(
'Launching reload command: ' . $reloadCmd);
510 Log::error(
'Config update failed for module: ' . $module);
513 }
else if (isset(
$response[
'success']) && $enhancedMode == 0) {
514 Log::debug(
'Enhanced mode disabled - skipping config update');
517 Log::debug(
'Clearing caches after module installation (Standard mode)...');
524 Log::error(
'No internet connection available.' );
526 return [
'error' =>
'No internet connection'];
544 Log::debug(
'Temporary Directory: ' . $tmpDir);
546 $fileName = basename($moduleUrl);
549 $tmpFilePath = $tmpDir .
'/' . $fileName;
552 $moduleName = str_replace(
'module-',
'', $module);
558 foreach ($this->modules as $key => $moduleInfo) {
559 if (strtolower($key) === strtolower($moduleName)) {
566 Log::error(
"Module not found in modules array: $moduleName");
567 return [
'error' =>
'Module configuration not found'];
570 $moduleType = $this->modules[$moduleKey][
'type'];
583 Log::error(
'Failed to retrieve file from URL: ' . $moduleUrl);
584 return [
'error' =>
'Failed to retrieve file from URL'];
588 $fileExtension = pathinfo($tmpFilePath, PATHINFO_EXTENSION);
589 Log::debug(
'File extension: ' . $fileExtension);
591 if ($fileExtension ===
'7z' || $fileExtension ===
'zip') {
592 echo json_encode([
'phase' =>
'extracting']) . PHP_EOL;
593 if (ob_get_length()) {
598 $unzipResult =
$bearsamppCore->unzipFile($tmpFilePath, $destination,
function ($currentPercentage) {
599 $progressStr = is_numeric($currentPercentage) ?
"$currentPercentage%" : $currentPercentage;
600 echo json_encode([
'progress' => $progressStr]) . PHP_EOL;
601 if (ob_get_length()) {
607 if ($unzipResult ===
false) {
608 return [
'error' =>
'Failed to unzip file. File: ' . $tmpFilePath .
' could not be unzipped',
'Destination: ' . $destination];
611 Log::error(
'Unsupported file extension: ' . $fileExtension);
612 return [
'error' =>
'Unsupported file extension'];
615 return [
'success' =>
'Module installed successfully'];
633 if ( $moduleType ===
'application' ) {
636 elseif ( $moduleType ===
'binary' ) {
639 elseif ( $moduleType ===
'tools' ) {
657 Log::debug(
'Regenerating menu (AJAX-safe mode)...');
660 $oldErrorReporting = error_reporting();
661 error_reporting($oldErrorReporting & ~E_WARNING);
668 error_reporting($oldErrorReporting);
673 }
catch (Exception $e) {
675 error_reporting($oldErrorReporting);
677 Log::warning(
'Error during menu regeneration: ' . $e->getMessage());
697 $moduleName = str_replace(
'module-',
'', $module);
702 foreach ($this->modules as $key => $moduleInfo) {
703 if (strtolower($key) === strtolower($moduleName)) {
710 Log::error(
"Module not found in modules array: $moduleName");
714 $moduleType = $this->modules[$moduleKey][
'type'];
718 $configSection = strtolower($moduleKey);
720 Log::debug(
"Updating config for module: $module (key: $moduleKey, type: $moduleType) to version: $version");
725 $configKey = $configSection .
'Version';
728 Log::info(
"Successfully updated $configSection version to $version in bearsampp.conf");
732 }
catch (Exception $e) {
733 Log::error(
"Failed to update module config: " . $e->getMessage());
749 <div
id=
"configErrorContainer" class=
"text-center mt-3 pe-3">
750 <div
class=
"alert alert-danger d-inline-block" role=
"alert" style=
"max-width: 500px;">
751 <h4
class=
"alert-heading">
752 <i
class=
"fas fa-exclamation-circle"></i> Configuration Error
756 <?php echo htmlspecialchars($errorMessage); ?>
759 <small
class=
"text-muted">
760 Please add the missing parameter to the <code>bearsampp.conf</code> file in the
Bearsampp root directory.
765 return ob_get_clean();
793 <div
class = "enhanced-mode-toggle
">
794 <label class = "form-check-label me-2
" for = "enhancedQuickPickSwitch
">
797 <div class = "form-check form-switch mb-0
">
798 <input class = "form-check-input
" type = "checkbox
" role = "switch
" id = "enhancedQuickPickSwitch
"
799 <?php echo $enhancedMode == 1 ? 'checked' : ''; ?>
800 data-bs-toggle = "tooltip
" data-bs-placement = "bottom
"
801 title = "Toggle between enhanced (auto-config update) and standard
QuickPick mode
">
804 <div id = 'quickPickContainer'>
805 <div class = 'quickpick'>
807 <div class = "custom-select
">
808 <button class = "select-button
" role = "combobox
"
809 aria-label = "select button
"
810 aria-haspopup = "listbox
"
811 aria-expanded = "false
"
812 aria-controls = "select-dropdown
">
813 <span class = "selected-value
">Select a module and version</span>
814 <span class = "arrow
"></span>
816 <ul class = "select-dropdown
" role = "listbox
" id = "select-dropdown
">
819 foreach ( $modules as $module ): ?>
820 <?php if ( is_string( $module ) ): ?>
821 <li role = "option
" class = "moduleheader
">
822 <?php echo htmlspecialchars( $module ); ?>
826 foreach ( $versions['module-' . strtolower( $module )] as $version_array ):
827 // Skip prerelease versions if includePr is not enabled
828 if (isset($version_array['prerelease']) && $version_array['prerelease'] === true && $includePr != 1) {
832 <li role = "option
" class = "moduleoption
"
833 id = "<?php echo htmlspecialchars( $module ); ?>-version-<?php echo htmlspecialchars( $version_array[
'version'] ); ?>-li
"
834 data-module = "<?php echo htmlspecialchars( $module ); ?>
"
835 data-value = "<?php echo htmlspecialchars( $version_array[
'version'] ); ?>
">
836 <input type = "radio
"
837 id = "<?php echo htmlspecialchars( $module ); ?>-version-<?php echo htmlspecialchars( $version_array[
'version'] ); ?>
"
838 name = "module
" data-module = "<?php echo htmlspecialchars( $module ); ?>
"
839 data-value = "<?php echo htmlspecialchars( $version_array[
'version'] ); ?>
">
841 for = "<?php echo htmlspecialchars( $module ); ?>-version-<?php echo htmlspecialchars( $version_array[
'version'] ); ?>
"><?php echo $this->formatVersionLabel( $version_array['version'], isset($version_array['prerelease']) && $version_array['prerelease'] === true ); ?></label>
849 <div class = "progress
" id = "progress
" tabindex = "-1
" style = "width:260px;display:none
"
850 aria-labelledby = "progressbar
" aria-hidden = "true">
851 <div class = "progress-bar progress-bar-striped progress-bar-animated
" id = "progress-bar
" role = "progressbar
" aria-valuenow = "0
" aria-valuemin = "0
"
852 aria-valuemax = "100
" data-module = "Module"
853 data-version = "0.0.0
">0%
855 <div id = "download-module
" style = "display: none
">ModuleName</div>
856 <div id = "download-version
" style = "display: none
">Version</div>
860 <div id = "subscribeContainer
" class = "text-center
">
861 <a href = "<?php echo
HttpClient::getWebsiteUrl(
'subscribe' ); ?>
" class = "btn btn-dark d-
inline-flex align-items-center
">
862 <img src = "<?php echo
$imagesPath .
'subscribe.svg'; ?>
" alt = "Subscribe Icon
" class = "me-2
">
863 Subscribe to QuickPick now
870 <div id = "InternetState
" class = "text-center
">
871 <img src = "<?php echo
$imagesPath .
'no-wifi-icon.svg'; ?>
" alt = "No Wifi Icon
" class = "me-2
">
872 <span>No internet present</span>
877 return ob_get_clean();
static background(string $command)
static getWebsiteUrl($path='', $fragment='', $utmSource=true)
static checkInternetState()
static info($data, $file=null)
static debug($data, $file=null)
static warning($data, $file=null)
static error($data, $file=null)
static getCorePath($aetrayPath=false)
static getToolsPath($aetrayPath=false)
static getResourcesPath($aetrayPath=false)
static getPhpExe($aetrayPath=false)
static getBinPath($aetrayPath=false)
static getTmpPath($aetrayPath=false)
static getAppsPath($aetrayPath=false)
isValidHeaderResponse($headers)
fetchAndUnzipModule(string $moduleUrl, string $module)
getModuleUrl(string $module, string $version)
getLocalFileCreationTime()
normalizeModuleName(string $moduleName)
getErrorModal(string $errorMessage)
installModule(string $module, string $version)
updateModuleConfig(string $module, string $version)
getModuleDestinationPath(string $moduleType, string $moduleName)
getQuickpickMenu(array $modules, array $versions, string $imagesPath)
formatVersionLabel($version, $isPrerelease=false)
logHeaders(array $headers)
loadQuickpick(string $imagesPath)