Skip to content

Commit

Permalink
Allow enabling/disabling modules during installer (#3273)
Browse files Browse the repository at this point in the history
  • Loading branch information
tadhgboyle authored Mar 7, 2023
1 parent 9cecbe2 commit 9b0ce79
Show file tree
Hide file tree
Showing 15 changed files with 156 additions and 15 deletions.
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ Deprecations rule of thumb:
* Squash when merging. This makes backporting easier. If you think your changes deserve multiple commits, consider splitting them into multiple pull requests.
* Mark the pull request with the appropriate milestone. [@Derkades](https://github.com/Derkades) keeps an eye on merged PRs and cherry-pick changes to the appropriate release branch.

## Adding new modules to core

After adding a new module to core, you need to do the following:
1. Update the `Dockerfile.phpdoc` file to include the new module classes folder (this generates our [PHPDoc](https://phpdoc.namelessmc.com/) site)
2. Update `composer.json` to autoload the new module classes folder
3. Add a new term to the `custom/languages/en_UK.json` file for the module description to be shown during instal
- The term should be in the format `module_{module_name}_description`
- Don't forget to add it to the `WHITELISTED_TERMS` array in `dev/scripts/find_unused_language_terms.sh`
4. Create new database entry to install it by default
- Update `core/installation/includes/upgrade_perform.php` around line 637
- Add a new entry in `core/classes/Database/DatabaseInitialiser.php` around line 83

## Releasing a new version

1. Ensure you have a clean copy of the source code without leftover files from testing. For example, clone the Nameless repository into a new directory
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.phpdoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ RUN mkdir /target && \
-d /source/modules/Core/classes \
-d "/source/modules/Discord Integration/classes" \
-d "/source/modules/Cookie Consent/classes" \
-d /source/modules/Forum/classes
-d /source/modules/Forum/classes \
-d /source/modules/Members/classes -t /target -i /vendor

FROM nginxinc/nginx-unprivileged:stable
Expand Down
2 changes: 1 addition & 1 deletion core/installation/installer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
if (!file_exists(__DIR__ . '/steps/' . $step . '.php')) {
$error = 'Unknown step.';
}

}

if (isset($step) && $step == 'ajax_initialise') {
Expand All @@ -39,6 +38,7 @@
create_step($language->get('installer', 'step_database_config'), 'server icon', ['database_configuration', 'database_initialization', 'upgrade', 'upgrade_perform']);
create_step($language->get('installer', 'step_site_config'), 'globe icon', ['site_configuration', 'site_initialization']);
create_step($language->get('installer', 'step_admin_account'), 'user icon', ['admin_account_setup']);
create_step($language->get('installer', 'step_select_modules'), 'puzzle piece icon', ['select_modules']);
create_step($language->get('installer', 'step_conversion'), 'exchange icon', ['conversion']);
create_step($language->get('installer', 'step_finish'), 'check icon', ['finish']);
?>
Expand Down
4 changes: 2 additions & 2 deletions core/installation/steps/admin_account_setup.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
if (isset($_SESSION['admin_setup']) && $_SESSION['admin_setup'] == true) {
Redirect::to('?step=conversion');
Redirect::to('?step=select_modules');
}

if (!isset($_SESSION['site_initialized']) || $_SESSION['site_initialized'] != true) {
Expand Down Expand Up @@ -113,7 +113,7 @@ function display_error(string $message) {
$_SESSION['admin_setup'] = true;
$user->addGroup(2);

Redirect::to('?step=conversion');
Redirect::to('?step=select_modules');
}

DB::getInstance()->delete('users', ['id', 1]);
Expand Down
4 changes: 2 additions & 2 deletions core/installation/steps/conversion.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
if (!isset($_SESSION['admin_setup']) || $_SESSION['admin_setup'] != true) {
Redirect::to('?step=admin_account_setup');
if (!isset($_SESSION['modules_selected']) || $_SESSION['modules_selected'] != true) {
Redirect::to('?step=select_modules');
}
?>

Expand Down
6 changes: 3 additions & 3 deletions core/installation/steps/finish.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?php

if (!isset($_SESSION['admin_setup']) || $_SESSION['admin_setup'] != true) {
Redirect::to('?step=admin_account_setup');
if (!isset($_SESSION['modules_selected']) || $_SESSION['modules_selected'] != true) {
Redirect::to('?step=select_modules');
}

try {
Config::set('core.installed', true);

unset($_SESSION['admin_setup'], $_SESSION['database_initialized'], $_SESSION['site_initialized']);
unset($_SESSION['admin_setup'], $_SESSION['database_initialized'], $_SESSION['site_initialized'], $_SESSION['modules_selected']);

} catch (Exception $e) {

Expand Down
108 changes: 108 additions & 0 deletions core/installation/steps/select_modules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php
if (isset($_SESSION['modules_selected']) && $_SESSION['modules_selected'] == true) {
Redirect::to('?step=conversion');
}

if (!isset($_SESSION['admin_setup']) || $_SESSION['admin_setup'] != true) {
Redirect::to('?step=admin_account_setup');
}

$all_modules = [];
foreach (scandir(ROOT_PATH . '/modules') as $module) {
if (!str_starts_with($module, '.') && is_dir(ROOT_PATH . '/modules/' . $module)) {
$all_modules[$module] = $language->get('installer', 'module_' . strtolower(str_replace(' ', '-', $module)) . '_description');
}
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$cache = new Cache(['name' => 'nameless', 'extension' => '.cache', 'path' => ROOT_PATH . '/cache/']);
$cache->setCache('modulescache');
$enabled_modules = array_filter($cache->retrieve('enabled_modules'), static function ($module) {
return Input::get('modules')[$module['name']] === '1' || $module['name'] === 'Core';
});
$cache->store('enabled_modules', $enabled_modules);

foreach (Input::get('modules') as $module => $value) {
if ($module === 'Core') {
continue;
}

DB::getInstance()->update('modules', ['name', $module], [
'enabled' => $value === '1',
]);
}

$_SESSION['modules_selected'] = true;
Redirect::to('?step=conversion');
}
?>

<form action="" method="post">
<div class="ui segments">
<div class="ui secondary segment">
<h4 class="ui header">
<?php echo $language->get('installer', 'select_modules'); ?>
</h4>
</div>
<div class="ui segment">
<p><?php echo $language->get('installer', 'select_modules_details'); ?></p>

<div class="ui two cards">
<?php foreach ($all_modules as $module => $description) { ?>
<div class="ui card fluid" data-module-name="<?php echo $module ?>" style="cursor: pointer; user-select: none; pointer-events: all !important;" onclick="toggleModule(this)">
<input type="hidden" name="modules[<?php echo $module ?>]" value="1">
<div class="content">
<div class="header">
<?php echo $module; ?>
<?php if ($module === 'Core') { ?>
<i class="ui lock icon yellow"></i>
<?php } else { ?>
<i class="ui check icon green"></i>
<?php } ?>
</div>
<div class="meta">
<?php echo $description ?>
</div>
</div>
</div>
<?php } ?>
</div>
</div>
<div class="ui right aligned secondary segment">
<button class="ui small primary button">
<?php echo $language->get('installer', 'proceed'); ?>
</button>
</div>
</div>
</form>

<script>
const selectedModules = [];

<?php foreach (array_keys($all_modules) as $module) { ?>
selectedModules.push('<?php echo $module ?>')
<?php } ?>

function toggleModule(element) {
const module = element.dataset.moduleName;
if (module === 'Core') {
return;
}

if (selectedModules.includes(module)) {
element.children[0].value = '0';
selectedModules.splice(selectedModules.indexOf(module), 1);
} else {
element.children[0].value = '1';
selectedModules.push(module);
}

element.classList.toggle('disabled');

const icon = element.children[1].children[0].children[0];
icon.classList.toggle('green');
icon.classList.toggle('red');
icon.classList.toggle('check');
icon.classList.toggle('x');
}
</script>
8 changes: 8 additions & 0 deletions custom/languages/en_UK.json
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,11 @@
"installer/installer_upgrading_database": "Please wait whilst the installer upgrades your database...",
"installer/installer_welcome": "Welcome to NamelessMC version 2.0",
"installer/language": "Language",
"installer/module_core_description": "This module is required for NamelessMC to function correctly.",
"installer/module_cookie-consent_description": "Allows users to accept the use of cookies on your website.",
"installer/module_forum_description": "Adds a forum to your website.",
"installer/module_discord-integration_description": "Allows users to link their Discord account to their website account and perform various synchronisation actions.",
"installer/module_members_description": "Adds a detailed members page and statistics to your website.",
"installer/new_installation": "New installation &raquo;",
"installer/new_installation_question": "Firstly, is this a new installation?",
"installer/no": "No",
Expand All @@ -927,6 +932,8 @@
"installer/reload_page": "Reload page",
"installer/requirements": "Requirements:",
"installer/requirements_error": "You must have all of the required extensions installed, and have correct permissions set, in order to proceed with installation.",
"installer/select_modules": "Select Modules",
"installer/select_modules_details": "Select the modules you would like to enable on your site. These can be toggled later on through the admin panel.",
"installer/session_doesnt_exist": "Unable to detect session. Sessions saving are a requirement to use Nameless. Please try again after clearing cookies in your web browser, and if the issue persists, please contact your web host for support.",
"installer/site_name": "Site Name",
"installer/step_admin_account": "Admin Account",
Expand All @@ -936,6 +943,7 @@
"installer/step_general_config": "General Configuration",
"installer/step_home": "Home",
"installer/step_requirements": "Requirements",
"installer/step_select_modules": "Select Modules",
"installer/step_site_config": "Site Configuration",
"installer/support_message": "If you need any support, check out our website: {{websiteLinkStart}}here{{websiteLinkEnd}}; you can also visit our Discord server: {{discordLinkStart}}Discord server{{discordLinkEnd}}, or our GitHub repository: {{githubLinkStart}}here{{githubLinkEnd}}",
"installer/terms_and_conditions": "By continuing you agree to the terms and conditions.",
Expand Down
13 changes: 13 additions & 0 deletions dev/scripts/find_unused_language_terms.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,26 @@ FILES=(
"modules/Discord Integration/language/en_UK.json"
"modules/Members/language/en_UK.json"
)
# terms which are too tricky to detect, or are used in a different way
WHITELISTED_TERMS=(
"installer/module_cookie-consent_description"
"installer/module_discord-integration_description"
"installer/module_forum_description"
"installer/module_core_description"
"installer/module_members_description"
)

for FILE in "${FILES[@]}"
do
echo "Checking $FILE for unused terms..."
KEYS=$(jq -M -r 'keys | .[]' "$FILE")
for KEY in $KEYS
do
# check if the term is whitelisted
if [[ " ${WHITELISTED_TERMS[*]} " =~ ${KEY} ]]; then
continue
fi

BEFORE_SLASH="${KEY%%/*}"
AFTER_SLASH="${KEY#*/}"

Expand Down
2 changes: 1 addition & 1 deletion modules/Cookie Consent/classes/CookieConsent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* CookieConsent module main class
*
* @package Modules\Cookie Consent
* @package Modules\CookieConsent
* @author Samerton
* @version 2.0.0-pr13
* @license MIT
Expand Down
2 changes: 1 addition & 1 deletion modules/Cookie Consent/classes/CookieConsent_Sitemap.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* CookieConsent sitemap class
*
* @package Modules\Cookie Consent
* @package Modules\CookieConsent
* @author Samerton
* @version 2.0.0-pr13
* @license MIT
Expand Down
2 changes: 1 addition & 1 deletion modules/Discord Integration/classes/Discord.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* Discord utility class
*
* @package Modules\Discord Integration
* @package Modules\DiscordIntegration
* @author Aberdeener
* @version 2.0.0-pr13
* @license MIT
Expand Down
2 changes: 1 addition & 1 deletion modules/Discord Integration/classes/DiscordApiErrors.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Contains namespaced API error messages for the Discord Integration module.
* These have no versioning, and are not meant to be used by any other modules.
*
* @package Modules\Discord Integration
* @package Modules\DiscordIntegration
* @author Aberdeener
* @version 2.0.0-pr13
* @license MIT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* Discord group sync injector implementation.
*
* @package Modules\Discord Integration
* @package Modules\DiscordIntegration
* @author Aberdeener
* @version 2.0.3
* @license MIT
Expand Down
2 changes: 1 addition & 1 deletion modules/Members/classes/MemberListProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* Base class for member list providers.
*
* @package Modules\Forum
* @package Modules\Members
* @author Aberdener
* @version 2.1.0
* @license MIT
Expand Down

0 comments on commit 9b0ce79

Please sign in to comment.