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']
72 $this->jsonFilePath =
$bearsamppCore->getResourcesPath() .
'/quickpick-releases.json';
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'])) {
240 $content = @file_get_contents( $this->jsonFilePath );
241 if ( $content ===
false ) {
242 Util::logError(
'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 Util::logError(
'Error decoding JSON content: ' . json_last_error_msg() );
251 return [
'error' =>
'Error decoding JSON content'];
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' );
319 return [
'error' =>
'No versions found'];
343 Util::logDebug(
'getModuleUrl called for module: ' . $module .
' version: ' . $version );
344 $url = trim( $this->versions[
'module-' . strtolower( $module )][$version][
'url'] );
346 Util::logDebug(
'Found URL for version: ' . $version .
' URL: ' . $url );
353 return [
'error' =>
'Version not found'];
389 if ( empty( $DownloadId ) ) {
405 Util::logError(
'Failed to validate QuickPick license - API server unavailable' );
414 if ( json_last_error() !== JSON_ERROR_NONE ) {
415 Util::logError(
'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 ) {
451 if ( is_array( $moduleUrl ) && isset( $moduleUrl[
'error'] ) ) {
452 Util::logError(
'Module URL not found for module: ' . $module .
' version: ' . $version );
454 return [
'error' =>
'Module URL not found'];
457 if ( empty( $moduleUrl ) ) {
458 Util::logError(
'Module URL not found for module: ' . $module .
' version: ' . $version );
460 return [
'error' =>
'Module URL not found'];
477 Util::logDebug(
'Enhanced mode enabled - Updating config for module: ' . $module .
' version: ' . $version);
480 if ($configUpdated) {
482 Util::logDebug(
'Config updated successfully, triggering reload to apply changes...');
485 $obLevel = ob_get_level();
486 while (ob_get_level() > 0) {
490 echo json_encode([
'phase' =>
'updating',
'message' =>
'Updating system configuration...']);
494 for ($i = 0; $i < $obLevel; $i++) {
499 Util::logDebug(
'Installation complete - user must manually reload from tray menu');
506 Util::logDebug(
'Enhanced mode disabled - skipping config update');
514 return [
'error' =>
'No internet connection'];
534 $fileName = basename($moduleUrl);
537 $tmpFilePath = $tmpDir .
'/' . $fileName;
540 $moduleName = str_replace(
'module-',
'', $module);
546 foreach ($this->modules as $key => $moduleInfo) {
547 if (strtolower($key) === strtolower($moduleName)) {
555 return [
'error' =>
'Module configuration not found'];
558 $moduleType = $this->modules[$moduleKey][
'type'];
571 Util::logError(
'Failed to retrieve file from URL: ' . $moduleUrl);
572 return [
'error' =>
'Failed to retrieve file from URL'];
576 $fileExtension = pathinfo($tmpFilePath, PATHINFO_EXTENSION);
579 if ($fileExtension ===
'7z' || $fileExtension ===
'zip') {
581 echo json_encode([
'phase' =>
'extracting']);
582 if (ob_get_length()) {
587 $unzipResult =
$bearsamppCore->unzipFile($tmpFilePath, $destination,
function ($currentPercentage) {
588 echo json_encode([
'progress' =>
"$currentPercentage%"]);
589 if (ob_get_length()) {
595 if ($unzipResult ===
false) {
596 return [
'error' =>
'Failed to unzip file. File: ' . $tmpFilePath .
' could not be unzipped',
'Destination: ' . $destination];
600 return [
'error' =>
'Unsupported file extension'];
603 return [
'success' =>
'Module installed successfully'];
621 if ( $moduleType ===
'application' ) {
622 $destination =
$bearsamppRoot->getAppsPath() .
'/' . strtolower( $moduleName ) .
'/';
624 elseif ( $moduleType ===
'binary' ) {
625 $destination =
$bearsamppRoot->getBinPath() .
'/' . strtolower( $moduleName ) .
'/';
627 elseif ( $moduleType ===
'tools' ) {
628 $destination =
$bearsamppRoot->getToolsPath() .
'/' . strtolower( $moduleName ) .
'/';
648 $oldErrorReporting = error_reporting();
649 error_reporting($oldErrorReporting & ~E_WARNING);
656 error_reporting($oldErrorReporting);
661 }
catch (Exception $e) {
663 error_reporting($oldErrorReporting);
685 $moduleName = str_replace(
'module-',
'', $module);
690 foreach ($this->modules as $key => $moduleInfo) {
691 if (strtolower($key) === strtolower($moduleName)) {
702 $moduleType = $this->modules[$moduleKey][
'type'];
706 $configSection = strtolower($moduleKey);
708 Util::logDebug(
"Updating config for module: $module (key: $moduleKey, type: $moduleType) to version: $version");
713 $configKey = $configSection .
'Version';
716 Util::logInfo(
"Successfully updated $configSection version to $version in bearsampp.conf");
720 }
catch (Exception $e) {
721 Util::logError(
"Failed to update module config: " . $e->getMessage());
737 <div
id=
"configErrorContainer" class=
"text-center mt-3 pe-3">
738 <div
class=
"alert alert-danger d-inline-block" role=
"alert" style=
"max-width: 500px;">
739 <h4
class=
"alert-heading">
740 <i
class=
"fas fa-exclamation-circle"></i> Configuration Error
744 <?php echo htmlspecialchars($errorMessage); ?>
747 <small
class=
"text-muted">
748 Please add the missing parameter to the <code>bearsampp.conf</code> file in the
Bearsampp root directory.
753 return ob_get_clean();
781 <div
id =
'quickPickContainer'>
782 <div
class = 'quickpick'>
784 <div class =
"custom-select">
785 <button
class = "select-button
" role = "combobox
"
786 aria-label = "select button
"
787 aria-haspopup = "listbox
"
788 aria-expanded = "false
"
789 aria-controls = "select-dropdown
">
790 <span class = "selected-value
">Select a module and version</span>
791 <span class = "arrow
"></span>
793 <ul class = "select-dropdown
" role = "listbox
" id = "select-dropdown
">
796 foreach ( $modules as $module ): ?>
797 <?php if ( is_string( $module ) ): ?>
798 <li role = "option
" class = "moduleheader
">
799 <?php echo htmlspecialchars( $module ); ?>
803 foreach ( $versions['module-' . strtolower( $module )] as $version_array ):
804 // Skip prerelease versions if includePr is not enabled
805 if (isset($version_array['prerelease']) && $version_array['prerelease'] === true && $includePr != 1) {
809 <li role = "option
" class = "moduleoption
"
810 id = "<?php echo htmlspecialchars( $module ); ?>-version-<?php echo htmlspecialchars( $version_array[
'version'] ); ?>-li
">
811 <input type = "radio
"
812 id = "<?php echo htmlspecialchars( $module ); ?>-version-<?php echo htmlspecialchars( $version_array[
'version'] ); ?>
"
813 name = "module
" data-module = "<?php echo htmlspecialchars( $module ); ?>
"
814 data-value = "<?php echo htmlspecialchars( $version_array[
'version'] ); ?>
">
816 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>
824 <div class = "progress
" id = "progress
" tabindex = "-1
" style = "width:260px;display:none
"
825 aria-labelledby = "progressbar
" aria-hidden = "true">
826 <div class = "progress-bar progress-bar-striped progress-bar-animated
" id = "progress-bar
" role = "progressbar
" aria-valuenow = "0
" aria-valuemin = "0
"
827 aria-valuemax = "100
" data-module = "Module"
828 data-version = "0.0.0
">0%
830 <div id = "download-module
" style = "display: none
">ModuleName</div>
831 <div id = "download-version
" style = "display: none
">Version</div>
835 <div id = "subscribeContainer
" class = "text-center mt-3 pe-3
">
836 <a href = "<?php echo
Util::getWebsiteUrl(
'subscribe' ); ?>
" class = "btn btn-dark d-
inline-flex align-items-center
">
837 <img src = "<?php echo
$imagesPath .
'subscribe.svg'; ?>
" alt = "Subscribe Icon
" class = "me-2
">
838 Subscribe to QuickPick now
845 <div id = "InternetState
" class = "text-center mt-3 pe-3
">
846 <img src = "<?php echo
$imagesPath .
'no-wifi-icon.svg'; ?>
" alt = "No Wifi Icon
" class = "me-2
">
847 <span>No internet present</span>
852 return ob_get_clean();
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)
static logError($data, $file=null)
static logInfo($data, $file=null)
static logDebug($data, $file=null)
static getWebsiteUrl($path='', $fragment='', $utmSource=true)
static checkInternetState()
static logWarning($data, $file=null)