Bearsampp 2026.7.28
Loading...
Searching...
No Matches
QuickPick Class Reference

Public Member Functions

 __construct ()
 checkDownloadId ()
 checkQuickpickJson ()
 fetchAndUnzipModule (string $moduleUrl, string $module)
 getErrorModal (string $errorMessage)
 getModuleDestinationPath (string $moduleType, string $moduleName)
 getModules ()
 getModuleUrl (string $module, string $version)
 getQuickpickJson ()
 getQuickpickMenu (array $modules, array $versions, string $imagesPath)
 getVersions ()
 installModule (string $module, string $version)
 loadQuickpick (string $imagesPath)
 normalizeModuleName (string $moduleName)
 rebuildQuickpickJson ()

Data Fields

 $modules

Private Member Functions

 formatVersionLabel ($version, $isPrerelease=false)
 getLocalFileCreationTime ()
 isValidHeaderResponse ($headers)
 logHeaders (array $headers)
 regenerateMenuSafe ()
 updateModuleConfig (string $module, string $version)

Private Attributes

 $jsonFilePath
 $versions = []

Detailed Description

Class QuickPick

The QuickPick class provides functionalities for managing and installing various modules within the Bearsampp application. It includes methods for retrieving available modules, fetching module versions, parsing release properties, and validating license keys.

Definition at line 18 of file class.action.quickPick.php.

Constructor & Destructor Documentation

◆ __construct()

__construct ( )

Constructor to initialize the jsonFilePath.

Definition at line 69 of file class.action.quickPick.php.

70 {
71 global $bearsamppCore;
72 $this->jsonFilePath = Path::getResourcesPath() . '/quickpick-releases.json';
73 }
global $bearsamppCore
static getResourcesPath($aetrayPath=false)

References $bearsamppCore, and Path\getResourcesPath().

Here is the call graph for this function:

Member Function Documentation

◆ checkDownloadId()

checkDownloadId ( )

Validates the format of a given username key by checking it against an external API.

This method performs several checks to ensure the validity of the username key:

  1. Logs the method call.
  2. Ensures the global configuration is available.
  3. Retrieves the username key from the global configuration.
  4. Ensures the username key is not empty.
  5. Constructs the API URL using the username key.
  6. Fetches the API response.
  7. Decodes the JSON response.
  8. Validates the response data.
Returns
bool True if the username key is valid, false otherwise.

Definition at line 372 of file class.action.quickPick.php.

372 : bool
373 {
374 global $bearsamppConfig;
375
376 Log::debug( 'checkDownloadId method called.' );
377
378 // Ensure the global config is available
379 if ( !isset( $bearsamppConfig ) ) {
380 Log::error( 'Global configuration is not set.' );
381
382 return false;
383 }
384
385 $DownloadId = $bearsamppConfig->getDownloadId();
386 Log::debug( 'DownloadId is: ' . $DownloadId );
387
388 // Ensure the license key is not empty
389 if ( empty( $DownloadId ) ) {
390 Log::error( 'License key is empty.' );
391
392 return false;
393 }
394
395 $url = QUICKPICK_API_URL . QUICKPICK_API_KEY . '&download_id=' . $DownloadId;
396 Log::debug( 'API URL: ' . $url );
397
398 // Attempt to fetch the API response
399 // Note: If this fails, PHP will generate a warning which will be logged by the error handler
400 // This is expected behavior when the API server is unavailable
401 $response = file_get_contents( $url );
402
403 // Check if the response is false
404 if ( $response === false ) {
405 Log::error( 'Failed to validate QuickPick license - API server unavailable' );
406 return false;
407 }
408
409 Log::debug( 'API response: ' . $response );
410
411 $data = json_decode( $response, true );
412
413 // Check if the JSON decoding was successful
414 if ( json_last_error() !== JSON_ERROR_NONE ) {
415 Log::error( 'Error decoding JSON response: ' . json_last_error_msg() );
416
417 return false;
418 }
419
420 // Validate the response data
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 );
423
424 return true;
425 }
426
427 Log::error( 'Invalid license key: ' . $DownloadId );
428
429 return false;
430 }
static debug($data, $file=null)
static error($data, $file=null)
global $bearsamppConfig
Definition homepage.php:41
const QUICKPICK_API_URL
Definition root.php:24
const QUICKPICK_API_KEY
Definition root.php:23

References $bearsamppConfig, $response, Log\debug(), Log\error(), QUICKPICK_API_KEY, and QUICKPICK_API_URL.

Referenced by getQuickpickMenu().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkQuickpickJson()

checkQuickpickJson ( )

Checks if the local quickpick-releases.json file is up-to-date with the remote version.

Compares the creation time of the local JSON file with the remote file's last modified time. If the remote file is newer or the local file does not exist, it fetches the latest JSON data by calling the rebuildQuickpickJson method.

Returns
array|false Returns the JSON data if the remote file is newer or the local file does not exist, otherwise returns false.
Exceptions
Exception

Definition at line 164 of file class.action.quickPick.php.

165 {
166 global $bearsamppConfig;
167
168 // Determine local file creation time or rebuild if missing
169 $localFileCreationTime = $this->getLocalFileCreationTime();
170
171 // Attempt to retrieve remote file headers
172 $headers = get_headers(QUICKPICK_JSON_URL, 1);
173 if (!$this->isValidHeaderResponse($headers)) {
174 // If headers or Date are invalid, assume no update needed
175 return false;
176 }
177
178 // Optionally log headers for verbose output
179 $this->logHeaders($headers);
180
181 // Compare the creation times (remote vs. local)
182 $remoteFileCreationTime = strtotime(isset($headers['Date']) ? $headers['Date'] : '');
183 if ($remoteFileCreationTime > $localFileCreationTime) { return $this->rebuildQuickpickJson(); }
184
185 // Return false if local file is already up-to-date
186 return false;
187 }
isValidHeaderResponse($headers)
logHeaders(array $headers)
const QUICKPICK_JSON_URL
Definition root.php:27

References $bearsamppConfig, getLocalFileCreationTime(), isValidHeaderResponse(), logHeaders(), QUICKPICK_JSON_URL, and rebuildQuickpickJson().

Referenced by loadQuickpick().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fetchAndUnzipModule()

fetchAndUnzipModule ( string $moduleUrl,
string $module )

Fetches the module URL and stores it in /tmp, then unzips the file based on its extension.

Parameters
string$moduleUrlThe URL of the module to fetch.
string$moduleThe name of the module.
Returns
array An array containing the status and message.

Definition at line 538 of file class.action.quickPick.php.

538 : array
539{
540 Log::debug("$module is: " . $module);
541
543 $tmpDir = Path::getTmpPath();
544 Log::debug('Temporary Directory: ' . $tmpDir);
545
546 $fileName = basename($moduleUrl);
547 Log::debug('File Name: ' . $fileName);
548
549 $tmpFilePath = $tmpDir . '/' . $fileName;
550 Log::debug('File Path: ' . $tmpFilePath);
551
552 $moduleName = str_replace('module-', '', $module);
553 Log::debug('Module Name: ' . $moduleName);
554
555 // Find the correct module key by searching through the modules array
556 // This handles proper capitalization for all module types
557 $moduleKey = null;
558 foreach ($this->modules as $key => $moduleInfo) {
559 if (strtolower($key) === strtolower($moduleName)) {
560 $moduleKey = $key;
561 break;
562 }
563 }
564
565 if (!$moduleKey) {
566 Log::error("Module not found in modules array: $moduleName");
567 return ['error' => 'Module configuration not found'];
568 }
569
570 $moduleType = $this->modules[$moduleKey]['type'];
571 Log::debug('Module Type: ' . $moduleType);
572
573 // Get module type
574 $destination = $this->getModuleDestinationPath($moduleType, $moduleName);
575 Log::debug('Destination: ' . $destination);
576
577 // Retrieve the file path from the URL using the bearsamppCore module,
578 // passing the module URL and temporary file path, with the use Progress Bar parameter set to true.
579 $result = $bearsamppCore->getFileFromUrl($moduleUrl, $tmpFilePath, true);
580
581 // Check if $result is false
582 if ($result === false) {
583 Log::error('Failed to retrieve file from URL: ' . $moduleUrl);
584 return ['error' => 'Failed to retrieve file from URL'];
585 }
586
587 // Determine the file extension and call the appropriate unzipping function
588 $fileExtension = pathinfo($tmpFilePath, PATHINFO_EXTENSION);
589 Log::debug('File extension: ' . $fileExtension);
590
591 if ($fileExtension === '7z' || $fileExtension === 'zip') {
592 echo json_encode(['phase' => 'extracting']) . PHP_EOL;
593 if (ob_get_length()) {
594 ob_flush();
595 }
596 flush();
597
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()) {
602 ob_flush();
603 }
604 flush();
605 });
606
607 if ($unzipResult === false) {
608 return ['error' => 'Failed to unzip file. File: ' . $tmpFilePath . ' could not be unzipped', 'Destination: ' . $destination];
609 }
610 } else {
611 Log::error('Unsupported file extension: ' . $fileExtension);
612 return ['error' => 'Unsupported file extension'];
613 }
614
615 return ['success' => 'Module installed successfully'];
616}
$result
global $bearsamppRoot
static getTmpPath($aetrayPath=false)
getModuleDestinationPath(string $moduleType, string $moduleName)

References $bearsamppCore, $bearsamppRoot, $result, Log\debug(), Log\error(), getModuleDestinationPath(), and Path\getTmpPath().

Referenced by installModule().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ formatVersionLabel()

formatVersionLabel ( $version,
$isPrerelease = false )
private

Format version label with PR indicator if it's a prerelease

Parameters
string$versionThe version to format
bool$isPrereleaseWhether this version is a prerelease
Returns
string Formatted version string

Definition at line 82 of file class.action.quickPick.php.

82 {
83 global $bearsamppConfig;
84 $includePr = $bearsamppConfig->getIncludePr();
85
86 if ($isPrerelease && $includePr == 1) {
87 return '<span class="text-danger">' . htmlspecialchars($version) . ' PR</span>';
88 }
89
90 return htmlspecialchars($version);
91 }

References $bearsamppConfig.

◆ getErrorModal()

getErrorModal ( string $errorMessage)

Generates an error modal for configuration validation failures.

Parameters
string$errorMessageThe error message to display.
Returns
string The HTML content of the error modal.

Definition at line 745 of file class.action.quickPick.php.

745 : string
746 {
747 ob_start();
748 ?>
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
753 </h4>
754 <hr>
755 <p class="mb-0">
756 <?php echo htmlspecialchars($errorMessage); ?>
757 </p>
758 <hr>
759 <small class="text-muted">
760 Please add the missing parameter to the <code>bearsampp.conf</code> file in the Bearsampp root directory.
761 </small>
762 </div>
763 </div>
764 <?php
765 return ob_get_clean();
766 }

Referenced by loadQuickpick().

Here is the caller graph for this function:

◆ getLocalFileCreationTime()

getLocalFileCreationTime ( )
private

Returns the local file's creation time, or triggers and returns 0 if file does not exist.

Returns
int Local file's creation time or 0 if the file doesn't exist.

Definition at line 194 of file class.action.quickPick.php.

195 {
196 if (!file_exists($this->jsonFilePath)) {
197 // If local file is missing, rebuild it immediately
198 $this->rebuildQuickpickJson();
199 return 0;
200 }
201 return filectime($this->jsonFilePath);
202 }

References rebuildQuickpickJson().

Referenced by checkQuickpickJson().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getModuleDestinationPath()

getModuleDestinationPath ( string $moduleType,
string $moduleName )

Get the destination path for a given module type and name.

This method constructs the destination path based on the type of module (application, binary, or tools) and the module name. It utilizes the bearsamppRoot global object to retrieve the base paths for each module type.

Parameters
string$moduleTypeThe type of the module ('application', 'binary', or 'tools').
string$moduleNameThe name of the module.
Returns
string The constructed destination path for the module.

Definition at line 630 of file class.action.quickPick.php.

631 {
632 global $bearsamppRoot;
633 if ( $moduleType === 'application' ) {
634 $destination = Path::getAppsPath() . '/' . strtolower( $moduleName ) . '/';
635 }
636 elseif ( $moduleType === 'binary' ) {
637 $destination = Path::getBinPath() . '/' . strtolower( $moduleName ) . '/';
638 }
639 elseif ( $moduleType === 'tools' ) {
640 $destination = Path::getToolsPath() . '/' . strtolower( $moduleName ) . '/';
641 }
642 else {
643 $destination = '';
644 }
645
646 return $destination;
647 }
static getToolsPath($aetrayPath=false)
static getBinPath($aetrayPath=false)
static getAppsPath($aetrayPath=false)

References $bearsamppRoot, Path\getAppsPath(), Path\getBinPath(), and Path\getToolsPath().

Referenced by fetchAndUnzipModule().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getModules()

getModules ( )

Retrieves the list of available modules.

Returns
array An array of module names.

Definition at line 121 of file class.action.quickPick.php.

121 : array
122 {
123 return array_keys( $this->modules );
124 }

Referenced by loadQuickpick().

Here is the caller graph for this function:

◆ getModuleUrl()

getModuleUrl ( string $module,
string $version )

Fetches the URL of a specified module version from the local quickpick-releases.json file.

This method reads the quickpick-releases.json file to find the URL associated with the given module and version. It logs the process and returns the URL if found, or an error message if not.

Parameters
string$moduleThe name of the module.
string$versionThe version of the module.
Returns
string|array The URL of the specified module version or an error message if the version is not found.

Definition at line 340 of file class.action.quickPick.php.

341 {
342 $this->getVersions();
343 Log::debug( 'getModuleUrl called for module: ' . $module . ' version: ' . $version );
344 $url = trim( $this->versions['module-' . strtolower( $module )][$version]['url'] );
345 if ( $url <> '' ) {
346 Log::debug( 'Found URL for version: ' . $version . ' URL: ' . $url );
347
348 return $url;
349 }
350 else {
351 Log::error( 'Version not found: ' . $version );
352
353 return ['error' => 'Version not found'];
354 }
355 }

References Log\debug(), Log\error(), and getVersions().

Referenced by installModule().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getQuickpickJson()

getQuickpickJson ( )

Retrieves the QuickPick JSON data from the local file.

Returns
array The decoded JSON data, or an error message if the file cannot be fetched or decoded.

Definition at line 238 of file class.action.quickPick.php.

238 : array
239 {
240 $content = @file_get_contents( $this->jsonFilePath );
241 if ( $content === false ) {
242 Log::error( 'Error fetching content from JSON file: ' . $this->jsonFilePath );
243
244 return ['error' => 'Error fetching JSON file'];
245 }
246
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() );
250
251 return ['error' => 'Error decoding JSON content'];
252 }
253
254 return $data;
255 }

References Log\error().

Referenced by getVersions().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getQuickpickMenu()

getQuickpickMenu ( array $modules,
array $versions,
string $imagesPath )

Generates the HTML content for the QuickPick menu.

This method creates the HTML structure for the QuickPick interface, including a dropdown for selecting modules and their respective versions. It checks if the license key is valid before displaying the modules. If the license key is invalid, it displays a subscription prompt. If there is no internet connection, it displays a message indicating the lack of internet.

Parameters
array$modulesAn array of available modules.
array$versionsAn associative array where the key is the module name and the value is an array containing the module versions.
string$imagesPathThe path to the images directory.
Returns
string The HTML content of the QuickPick menu.

Definition at line 782 of file class.action.quickPick.php.

782 : string
783 {
784 global $bearsamppConfig;
785 $includePr = $bearsamppConfig->getIncludePr();
786 $enhancedMode = $bearsamppConfig->getEnhancedQuickPick();
787
788 ob_start();
790
791 // Check if the license key is valid
792 if ( $this->checkDownloadId() ): ?>
793 <div class = "enhanced-mode-toggle">
794 <label class = "form-check-label me-2" for = "enhancedQuickPickSwitch">
795 Enhanced Mode
796 </label>
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">
802 </div>
803 </div>
804 <div id = 'quickPickContainer'>
805 <div class = 'quickpick'>
806
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>
815 </button>
816 <ul class = "select-dropdown" role = "listbox" id = "select-dropdown">
817
818 <?php
819 foreach ( $modules as $module ): ?>
820 <?php if ( is_string( $module ) ): ?>
821 <li role = "option" class = "moduleheader">
822 <?php echo htmlspecialchars( $module ); ?>
823 </li>
824
825 <?php
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) {
829 continue;
830 }
831 ?>
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'] ); ?>">
840 <label
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>
842 </li>
843 <?php endforeach; ?>
844 <?php endif; ?>
845 <?php endforeach; ?>
846 </ul>
847 </div>
848 </div>
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%
854 </div>
855 <div id = "download-module" style = "display: none">ModuleName</div>
856 <div id = "download-version" style = "display: none">Version</div>
857 </div>
858 </div>
859 <?php else: ?>
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
864 </a>
865 </div>
866 <?php endif;
867 }
868 else {
869 ?>
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>
873 </div>
874 <?php
875 }
876
877 return ob_get_clean();
878 }
static getWebsiteUrl($path='', $fragment='', $utmSource=true)
static checkInternetState()
$imagesPath
Definition homepage.php:52

References $bearsamppConfig, $imagesPath, $modules, $versions, checkDownloadId(), HttpClient\checkInternetState(), and HttpClient\getWebsiteUrl().

Referenced by loadQuickpick().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getVersions()

getVersions ( )

Retrieves the list of available versions for all modules.

This method fetches the QuickPick JSON data and returns an array of versions or If no versions are found, an error message is logged and returned.

Returns
array An array of version strings for the specified module, or an error message if no versions are found.

Definition at line 295 of file class.action.quickPick.php.

295 : array
296 {
297 Log::debug( 'Versions called' );
298
299 $versions = [];
300
301 $jsonData = $this->getQuickpickJson();
302
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' );
308 }
309 }
310 }
311 else {
312 Log::error( 'Invalid entry format in JSON data' );
313 }
314 }
315
316 if ( empty( $versions ) ) {
317 Log::error( 'No versions found' );
318
319 return ['error' => 'No versions found'];
320 }
321
322 Log::debug( 'Found versions' );
323
324 $this->versions = $versions;
325
326 return $versions;
327 }

References $versions, Log\debug(), Log\error(), and getQuickpickJson().

Referenced by getModuleUrl(), and loadQuickpick().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ installModule()

installModule ( string $module,
string $version )

Installs a specified module by fetching its URL and unzipping its contents.

This method retrieves the URL of the specified module and version from the QuickPick JSON data. If the URL is found, it fetches and unzips the module. If the URL is not found, it logs an error and returns an error message.

Parameters
string$moduleThe name of the module to install.
string$versionThe version of the module to install.
Returns
array An array containing the status and message of the installation process. If successful, it returns the response from the fetchAndUnzipModule method. If unsuccessful, it returns an error message indicating the issue.

Definition at line 446 of file class.action.quickPick.php.

446 : array
447 {
448 // Find the module URL and module name from the data
449 $moduleUrl = $this->getModuleUrl( $module, $version );
450
451 if ( is_array( $moduleUrl ) && isset( $moduleUrl['error'] ) ) {
452 Log::error( 'Module URL not found for module: ' . $module . ' version: ' . $version );
453
454 return ['error' => 'Module URL not found'];
455 }
456
457 if ( empty( $moduleUrl ) ) {
458 Log::error( 'Module URL not found for module: ' . $module . ' version: ' . $version );
459
460 return ['error' => 'Module URL not found'];
461 }
462
464 if ( $state ) {
465 $response = $this->fetchAndUnzipModule( $moduleUrl, $module );
466 Log::debug( 'Response is: ' . print_r( $response, true ) );
467
468 // Check if enhanced mode is enabled
469 global $bearsamppConfig;
470 $enhancedMode = $bearsamppConfig->getEnhancedQuickPick();
471
472 Log::debug('Enhanced mode: ' . ($enhancedMode ? 'enabled' : 'disabled'));
473
474 // If installation was successful and enhanced mode is enabled, update config
475 if (isset($response['success']) && $enhancedMode == 1) {
476 // Step 1: Update config FIRST (so reload can pick up the new version)
477 Log::debug('Enhanced mode enabled - Updating config for module: ' . $module . ' version: ' . $version);
478 $configUpdated = $this->updateModuleConfig($module, $version);
479
480 if ($configUpdated) {
481 // Step 2: Launch the reload action to apply the new version automatically.
482 // QuickPick runs in the AJAX/web context, where the winbinder GUI used by
483 // the reload action is unavailable, so we spawn it as a detached process
484 // (the same "php-win.exe root.php reload" command the tray menu runs). The
485 // reload restarts the database services so clients such as phpMyAdmin pick
486 // up the new version without the user having to reload manually.
487 Log::debug('Config updated successfully, launching reload to apply changes...');
488
489 // Send progress update to user - flush output
490 if (ob_get_level() > 0) {
491 ob_flush();
492 }
493 echo json_encode(['phase' => 'updating', 'message' => 'Applying version changes...']) . PHP_EOL;
494 flush();
495
496 // Clear caches before the reload runs so it reads fresh values from disk
497 Log::debug('Clearing caches before reload...');
499
500 // Build and launch the reload command detached from this request.
501 // Leading "" is the (empty) window title required by cmd.exe "start".
502 $reloadCmd = '"" "' . Path::getPhpExe() . '" "'
503 . Path::getCorePath() . '/' . Core::isRoot_FILE . '" '
505 Log::debug('Launching reload command: ' . $reloadCmd);
506 CommandRunner::background($reloadCmd);
507
508 $response['reload_triggered'] = true;
509 } else {
510 Log::error('Config update failed for module: ' . $module);
511 $response['reload_triggered'] = false;
512 }
513 } else if (isset($response['success']) && $enhancedMode == 0) {
514 Log::debug('Enhanced mode disabled - skipping config update');
515
516 // Even if not updating config, clear cache to be safe as new files were added
517 Log::debug('Clearing caches after module installation (Standard mode)...');
519 }
520
521 return $response;
522 }
523 else {
524 Log::error( 'No internet connection available.' );
525
526 return ['error' => 'No internet connection'];
527 }
528 }
const RELOAD
static background(string $command)
const isRoot_FILE
static getCorePath($aetrayPath=false)
static getPhpExe($aetrayPath=false)
fetchAndUnzipModule(string $moduleUrl, string $module)
getModuleUrl(string $module, string $version)
updateModuleConfig(string $module, string $version)

References $bearsamppConfig, $response, CommandRunner\background(), HttpClient\checkInternetState(), CacheManager\clearAll(), Log\debug(), Log\error(), fetchAndUnzipModule(), Path\getCorePath(), getModuleUrl(), Path\getPhpExe(), Core\isRoot_FILE, Action\RELOAD, and updateModuleConfig().

Here is the call graph for this function:

◆ isValidHeaderResponse()

isValidHeaderResponse ( $headers)
private

Determines whether the header response is valid and includes a 'Date' key.

Parameters
mixed$headersHeaders retrieved from get_headers().
Returns
bool True if headers are valid and contain 'Date', false otherwise.

Definition at line 210 of file class.action.quickPick.php.

210 : bool
211 {
212 // If headers retrieval failed or Date is not set, return false
213 if ($headers === false || !isset($headers['Date'])) {
214 return false;
215 }
216 return true;
217 }

Referenced by checkQuickpickJson().

Here is the caller graph for this function:

◆ loadQuickpick()

loadQuickpick ( string $imagesPath)

Loads the QuickPick interface with the available modules and their versions.

Parameters
string$imagesPathThe path to the images directory.
Returns
string The HTML content of the QuickPick interface.
Exceptions
Exception

Definition at line 135 of file class.action.quickPick.php.

135 : string
136 {
137 global $bearsamppConfig;
138
139 // Validate EnhancedQuickPick parameter
140 $validation = $bearsamppConfig->validateEnhancedQuickPick();
141 if (!$validation['valid']) {
142 return $this->getErrorModal($validation['error']);
143 }
144
145 $this->checkQuickpickJson();
146
147 $modules = $this->getModules();
148 $versions = $this->getVersions();
149
151 }
getErrorModal(string $errorMessage)
getQuickpickMenu(array $modules, array $versions, string $imagesPath)

References $bearsamppConfig, $imagesPath, $modules, $versions, checkQuickpickJson(), getErrorModal(), getModules(), getQuickpickMenu(), and getVersions().

Here is the call graph for this function:

◆ logHeaders()

logHeaders ( array $headers)
private

Logs the headers in debug mode if logsVerbose is set to 2.

Parameters
array$headersThe headers returned by get_headers().

Definition at line 224 of file class.action.quickPick.php.

224 : void
225 {
226 global $bearsamppConfig;
227
228 if ($bearsamppConfig->getLogsVerbose() === 2) {
229 Log::debug('Headers: ' . print_r($headers, true));
230 }
231 }

References $bearsamppConfig, and Log\debug().

Referenced by checkQuickpickJson().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ normalizeModuleName()

normalizeModuleName ( string $moduleName)

Normalizes a module name to find the correct module key from the modules array. Handles case-insensitive matching for all module types.

Parameters
string$moduleNameThe module name to normalize (may include 'module-' prefix)
Returns
string|null The correctly capitalized module key, or null if not found

Definition at line 100 of file class.action.quickPick.php.

100 : ?string
101 {
102 // Remove 'module-' prefix if present
103 $moduleName = str_replace('module-', '', $moduleName);
104
105 // Find the correct module key by searching through the modules array
106 // This handles proper capitalization for all module types
107 foreach ($this->modules as $key => $moduleInfo) {
108 if (strtolower($key) === strtolower($moduleName)) {
109 return $key;
110 }
111 }
112
113 return null;
114 }

◆ rebuildQuickpickJson()

rebuildQuickpickJson ( )

Rebuilds the local quickpick-releases.json file by fetching the latest data from the remote URL.

Returns
array An array containing the status and message of the rebuild process.
Exceptions
ExceptionIf the JSON content cannot be fetched or saved.

Definition at line 263 of file class.action.quickPick.php.

263 : array
264 {
265 Log::debug( 'Fetching JSON file: ' . $this->jsonFilePath );
266
267 // Fetch the JSON content from the URL
268 $jsonContent = file_get_contents( QUICKPICK_JSON_URL );
269
270 if ( $jsonContent === false ) {
271 // Handle error if the file could not be fetched
272 throw new Exception( 'Failed to fetch JSON content from the URL.' );
273 }
274
275 // Save the JSON content to the specified path
276 $result = file_put_contents( $this->jsonFilePath, $jsonContent );
277
278 if ( $result === false ) {
279 // Handle error if the file could not be saved
280 throw new Exception( 'Failed to save JSON content to the specified path.' );
281 }
282
283 // Return success message
284 return ['success' => 'JSON content fetched and saved successfully'];
285 }

References $result, Log\debug(), and QUICKPICK_JSON_URL.

Referenced by checkQuickpickJson(), and getLocalFileCreationTime().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ regenerateMenuSafe()

regenerateMenuSafe ( )
private

Regenerates the bearsampp.ini menu file without VBS checks (AJAX-safe version). This is a simplified version of TplApp\process() that avoids VBS errors in web context.

Returns
string The generated INI content

Definition at line 655 of file class.action.quickPick.php.

655 : string
656 {
657 Log::debug('Regenerating menu (AJAX-safe mode)...');
658
659 // Suppress errors temporarily during menu generation
660 $oldErrorReporting = error_reporting();
661 error_reporting($oldErrorReporting & ~E_WARNING);
662
663 try {
664 // Generate the menu content
665 $menuContent = TplApp::process();
666
667 // Restore error reporting
668 error_reporting($oldErrorReporting);
669
670 Log::debug('Menu regenerated successfully');
671 return $menuContent;
672
673 } catch (Exception $e) {
674 // Restore error reporting
675 error_reporting($oldErrorReporting);
676
677 Log::warning('Error during menu regeneration: ' . $e->getMessage());
678 throw $e;
679 }
680 }
static warning($data, $file=null)
static process()

References Log\debug(), TplApp\process(), and Log\warning().

Here is the call graph for this function:

◆ updateModuleConfig()

updateModuleConfig ( string $module,
string $version )
private

Updates the bearsampp.conf configuration file with the new module version. This method handles all module types: binaries, apps, and tools.

Parameters
string$moduleThe name of the module (e.g., 'Apache', 'PhpMyAdmin', 'Git').
string$versionThe version to set in the configuration.
Returns
bool True if the configuration was updated successfully, false otherwise.

Definition at line 691 of file class.action.quickPick.php.

691 : bool
692 {
693 try {
694 $bearsamppConfig = new Config();
695
696 // Remove 'module-' prefix if present and normalize the module name
697 $moduleName = str_replace('module-', '', $module);
698
699 // Find the correct module key by searching through the modules array
700 // This handles proper capitalization for all module types
701 $moduleKey = null;
702 foreach ($this->modules as $key => $moduleInfo) {
703 if (strtolower($key) === strtolower($moduleName)) {
704 $moduleKey = $key;
705 break;
706 }
707 }
708
709 if (!$moduleKey) {
710 Log::error("Module not found in modules array: $moduleName");
711 return false;
712 }
713
714 $moduleType = $this->modules[$moduleKey]['type'];
715
716 // Map module names to their config section names
717 // For all types, use the lowercase name for the config key
718 $configSection = strtolower($moduleKey);
719
720 Log::debug("Updating config for module: $module (key: $moduleKey, type: $moduleType) to version: $version");
721 Log::debug("Config section: $configSection");
722
723 // Update the configuration file
724 // The Config class expects a flat key like "nodejsVersion" not a section
725 $configKey = $configSection . 'Version';
726 $bearsamppConfig->replace($configKey, $version);
727
728 Log::info("Successfully updated $configSection version to $version in bearsampp.conf");
729
730 return true;
731
732 } catch (Exception $e) {
733 Log::error("Failed to update module config: " . $e->getMessage());
734 return false;
735 }
736 }
static info($data, $file=null)

References $bearsamppConfig, Log\debug(), Log\error(), and Log\info().

Referenced by installModule().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ $jsonFilePath

$jsonFilePath
private

The file path to the local quickpick-releases.json file.

Definition at line 64 of file class.action.quickPick.php.

◆ $modules

$modules
Initial value:
= [
'Apache' => ['type' => 'binary'],
'Bruno' => ['type' => 'tools'],
'Composer' => ['type' => 'tools'],
'Ghostscript' => ['type' => 'tools'],
'Git' => ['type' => 'tools'],
'Mailpit' => ['type' => 'binary'],
'MariaDB' => ['type' => 'binary'],
'Memcached' => ['type' => 'binary'],
'MySQL' => ['type' => 'binary'],
'Ngrok' => ['type' => 'tools'],
'NodeJS' => ['type' => 'binary'],
'Perl' => ['type' => 'tools'],
'PHP' => ['type' => 'binary'],
'PhpMyAdmin' => ['type' => 'application'],
'PhpPgAdmin' => ['type' => 'application'],
'PostgreSQL' => ['type' => 'binary'],
'PowerShell' => ['type' => 'tools'],
'Python' => ['type' => 'tools'],
'Ruby' => ['type' => 'tools'],
'Xlight' => ['type' => 'binary']
]

An associative array where the key is the module name and the value is an array containing the module type. The module type can be one of the following:

  • 'application'
  • 'binary'
  • 'tool'

Definition at line 29 of file class.action.quickPick.php.

Referenced by getQuickpickMenu(), and loadQuickpick().

◆ $versions

$versions = []
private

An associative array where the key is the module name and the value is an array containing the module versions.

Definition at line 57 of file class.action.quickPick.php.

Referenced by getQuickpickMenu(), getVersions(), and loadQuickpick().


The documentation for this class was generated from the following file: