1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/webtrees_ynh.git synced 2024-09-03 18:26:37 +02:00
webtrees_ynh/sources/app/Module.php

467 lines
15 KiB
PHP

<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Module\AbstractModule;
use Fisharebest\Webtrees\Module\ModuleBlockInterface;
use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleMenuInterface;
use Fisharebest\Webtrees\Module\ModuleReportInterface;
use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
use Fisharebest\Webtrees\Module\ModuleTabInterface;
use Fisharebest\Webtrees\Module\ModuleThemeInterface;
/**
* Functions for managing and maintaining modules.
*/
class Module {
/**
* Get a list of all core modules. We need to identify
* third-party during upgrade and on the module admin page.
*
* @return string[]
*/
public static function getCoreModuleNames() {
return array(
'GEDFact_assistant',
'ahnentafel_report',
'ancestors_chart',
'batch_update',
'bdm_report',
'birth_report',
'cemetery_report',
'change_report',
'charts',
'ckeditor',
'clippings',
'compact_tree_chart',
'death_report',
'descendancy',
'descendancy_chart',
'descendancy_report',
'extra_info',
'fact_sources',
'families',
'family_book_chart',
'family_group_report',
'family_nav',
'fan_chart',
'faq',
'gedcom_block',
'gedcom_favorites',
'gedcom_news',
'gedcom_stats',
'googlemap',
'hourglass_chart',
'html',
'individual_ext_report',
'individual_report',
'individuals',
'lifespans_chart',
'lightbox',
'logged_in',
'login_block',
'marriage_report',
'media',
'missing_facts_report',
'notes',
'occupation_report',
'page_menu',
'pedigree_chart',
'pedigree_report',
'personal_facts',
'random_media',
'recent_changes',
'relationships_chart',
'relative_ext_report',
'relatives',
'review_changes',
'sitemap',
'sources_tab',
'statistics_chart',
'stories',
'theme_select',
'timeline_chart',
'todays_events',
'todo',
'top10_givnnames',
'top10_pageviews',
'top10_surnames',
'tree',
'upcoming_events',
'user_blog',
'user_favorites',
'user_messages',
'user_welcome',
'yahrzeit',
);
}
/**
* Get a list of all active (enabled) modules.
*
* @return AbstractModule[]
*/
private static function getActiveModules() {
/** @var AbstractModule[] - Only query the database once. */
static $modules;
if ($modules === null) {
$module_names = Database::prepare(
"SELECT SQL_CACHE module_name FROM `##module` WHERE status = 'enabled'"
)->fetchOneColumn();
$modules = array();
foreach ($module_names as $module_name) {
try {
$module = include WT_ROOT . WT_MODULES_DIR . $module_name . '/module.php';
if ($module instanceof AbstractModule) {
$modules[$module->getName()] = $module;
} else {
throw new \Exception;
}
} catch (\Exception $ex) {
// The module has been deleted or is broken? Disable it.
Log::addConfigurationLog("Module {$module_name} is missing or broken - disabling it");
Database::prepare(
"UPDATE `##module` SET status = 'disabled' WHERE module_name = :module_name"
)->execute(array(
'module_name' => $module_name,
));
}
}
}
return $modules;
}
/**
* Get a list of modules which (a) provide a specific function and (b) we have permission to see.
*
* We cannot currently use auto-loading for modules, as there may be user-defined
* modules about which the auto-loader knows nothing.
*
* @param Tree $tree
* @param string $component The type of module, such as "tab", "report" or "menu"
*
* @return AbstractModule[]
*/
private static function getActiveModulesByComponent(Tree $tree, $component) {
$module_names = Database::prepare(
"SELECT SQL_CACHE module_name" .
" FROM `##module`" .
" JOIN `##module_privacy` USING (module_name)" .
" WHERE gedcom_id = :tree_id AND component = :component AND status = 'enabled' AND access_level >= :access_level" .
" ORDER BY CASE component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name"
)->execute(array(
'tree_id' => $tree->getTreeId(),
'component' => $component,
'access_level' => Auth::accessLevel($tree),
))->fetchOneColumn();
$array = array();
foreach ($module_names as $module_name) {
$interface = '\Fisharebest\Webtrees\Module\Module' . ucfirst($component) . 'Interface';
$module = self::getModuleByName($module_name);
if ($module instanceof $interface) {
$array[$module_name] = $module;
}
}
// The order of menus/sidebars/tabs is defined in the database. Others are sorted by name.
if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') {
uasort($array, function (AbstractModule $x, AbstractModule $y) {
return I18N::strcasecmp($x->getTitle(), $y->getTitle());
});
}
return $array;
}
/**
* Get a list of all modules, enabled or not, which provide a specific function.
*
* We cannot currently use auto-loading for modules, as there may be user-defined
* modules about which the auto-loader knows nothing.
*
* @param string $component The type of module, such as "tab", "report" or "menu"
*
* @return AbstractModule[]
*/
public static function getAllModulesByComponent($component) {
$module_names = Database::prepare(
"SELECT SQL_CACHE module_name" .
" FROM `##module`" .
" ORDER BY CASE :component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name"
)->execute(array(
'component' => $component,
))->fetchOneColumn();
$array = array();
foreach ($module_names as $module_name) {
$interface = '\Fisharebest\Webtrees\Module\Module' . ucfirst($component) . 'Interface';
$module = self::getModuleByName($module_name);
if ($module instanceof $interface) {
$array[$module_name] = $module;
}
}
// The order of menus/sidebars/tabs is defined in the database. Others are sorted by name.
if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') {
uasort($array, function (AbstractModule $x, AbstractModule $y) {
return I18N::strcasecmp($x->getTitle(), $y->getTitle());
});
}
return $array;
}
/**
* Get a list of modules which (a) provide a block and (b) we have permission to see.
*
* @param Tree $tree
*
* @return ModuleBlockInterface[]
*/
public static function getActiveBlocks(Tree $tree) {
return self::getActiveModulesByComponent($tree, 'block');
}
/**
* Get a list of modules which (a) provide a chart and (b) we have permission to see.
*
* @param Tree $tree
*
* @return ModuleChartInterface[]
*/
public static function getActiveCharts(Tree $tree) {
return self::getActiveModulesByComponent($tree, 'chart');
}
/**
* Get a list of modules which (a) provide a chart and (b) we have permission to see.
*
* @param Tree $tree
* @param string $module
*
* @return bool
*/
public static function isActiveChart(Tree $tree, $module) {
return array_key_exists($module, self::getActiveModulesByComponent($tree, 'chart'));
}
/**
* Get a list of modules which (a) provide a menu and (b) we have permission to see.
*
* @param Tree $tree
*
* @return ModuleMenuInterface[]
*/
public static function getActiveMenus(Tree $tree) {
return self::getActiveModulesByComponent($tree, 'menu');
}
/**
* Get a list of modules which (a) provide a report and (b) we have permission to see.
*
* @param Tree $tree
*
* @return ModuleReportInterface[]
*/
public static function getActiveReports(Tree $tree) {
return self::getActiveModulesByComponent($tree, 'report');
}
/**
* Get a list of modules which (a) provide a sidebar and (b) we have permission to see.
*
* @param Tree $tree
*
* @return ModuleSidebarInterface[]
*/
public static function getActiveSidebars(Tree $tree) {
return self::getActiveModulesByComponent($tree, 'sidebar');
}
/**
* Get a list of modules which (a) provide a tab and (b) we have permission to see.
*
* @param Tree $tree
*
* @return ModuleTabInterface[]
*/
public static function getActiveTabs(Tree $tree) {
return self::getActiveModulesByComponent($tree, 'tab');
}
/**
* Get a list of modules which (a) provide a theme and (b) we have permission to see.
*
* @param Tree $tree
*
* @return ModuleThemeInterface[]
*/
public static function getActiveThemes(Tree $tree) {
return self::getActiveModulesByComponent($tree, 'theme');
}
/**
* Find a specified module, if it is currently active.
*
* @param string $module_name
*
* @return AbstractModule|null
*/
public static function getModuleByName($module_name) {
$modules = self::getActiveModules();
if (array_key_exists($module_name, $modules)) {
return $modules[$module_name];
} else {
return null;
}
}
/**
* Scan the source code to find a list of all installed modules.
*
* During setup, new modules need a status of “enabled”.
* In admin->modules, new modules need status of “disabled”.
*
* @param string $default_status
*
* @return AbstractModule[]
*/
public static function getInstalledModules($default_status) {
$modules = array();
foreach (glob(WT_ROOT . WT_MODULES_DIR . '*/module.php') as $file) {
try {
$module = include $file;
if ($module instanceof AbstractModule) {
$modules[$module->getName()] = $module;
Database::prepare("INSERT IGNORE INTO `##module` (module_name, status, menu_order, sidebar_order, tab_order) VALUES (?, ?, ?, ?, ?)")->execute(array(
$module->getName(),
$default_status,
$module instanceof ModuleMenuInterface ? $module->defaultMenuOrder() : null,
$module instanceof ModuleSidebarInterface ? $module->defaultSidebarOrder() : null,
$module instanceof ModuleTabInterface ? $module->defaultTabOrder() : null,
));
// Set the default privcy for this module. Note that this also sets it for the
// default family tree, with a gedcom_id of -1
if ($module instanceof ModuleMenuInterface) {
Database::prepare(
"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
" SELECT ?, gedcom_id, 'menu', ?" .
" FROM `##gedcom`"
)->execute(array($module->getName(), $module->defaultAccessLevel()));
}
if ($module instanceof ModuleSidebarInterface) {
Database::prepare(
"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
" SELECT ?, gedcom_id, 'sidebar', ?" .
" FROM `##gedcom`"
)->execute(array($module->getName(), $module->defaultAccessLevel()));
}
if ($module instanceof ModuleTabInterface) {
Database::prepare(
"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
" SELECT ?, gedcom_id, 'tab', ?" .
" FROM `##gedcom`"
)->execute(array($module->getName(), $module->defaultAccessLevel()));
}
if ($module instanceof ModuleBlockInterface) {
Database::prepare(
"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
" SELECT ?, gedcom_id, 'block', ?" .
" FROM `##gedcom`"
)->execute(array($module->getName(), $module->defaultAccessLevel()));
}
if ($module instanceof ModuleChartInterface) {
Database::prepare(
"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
" SELECT ?, gedcom_id, 'chart', ?" .
" FROM `##gedcom`"
)->execute(array($module->getName(), $module->defaultAccessLevel()));
}
if ($module instanceof ModuleReportInterface) {
Database::prepare(
"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
" SELECT ?, gedcom_id, 'report', ?" .
" FROM `##gedcom`"
)->execute(array($module->getName(), $module->defaultAccessLevel()));
}
if ($module instanceof ModuleThemeInterface) {
Database::prepare(
"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
" SELECT ?, gedcom_id, 'theme', ?" .
" FROM `##gedcom`"
)->execute(array($module->getName(), $module->defaultAccessLevel()));
}
}
} catch (\Exception $ex) {
// Old or invalid module?
}
}
return $modules;
}
/**
* After creating a new family tree, we need to assign the default access
* rights for each module.
*
* @param int $tree_id
*/
public static function setDefaultAccess($tree_id) {
foreach (self::getInstalledModules('disabled') as $module) {
if ($module instanceof ModuleMenuInterface) {
Database::prepare(
"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'menu', ?)"
)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
}
if ($module instanceof ModuleSidebarInterface) {
Database::prepare(
"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'sidebar', ?)"
)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
}
if ($module instanceof ModuleTabInterface) {
Database::prepare(
"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'tab', ?)"
)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
}
if ($module instanceof ModuleBlockInterface) {
Database::prepare(
"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'block', ?)"
)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
}
if ($module instanceof ModuleChartInterface) {
Database::prepare(
"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'chart', ?)"
)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
}
if ($module instanceof ModuleReportInterface) {
Database::prepare(
"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'report', ?)"
)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
}
if ($module instanceof ModuleThemeInterface) {
Database::prepare(
"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'theme', ?)"
)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
}
}
}
}