1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2019 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees\Module; 19 20use Fisharebest\Webtrees\Auth; 21use Fisharebest\Webtrees\Database; 22use Fisharebest\Webtrees\Tree; 23use Illuminate\Database\Capsule\Manager as DB; 24use Symfony\Component\HttpFoundation\Response; 25 26/** 27 * Class AbstractModule - common functions for blocks 28 */ 29abstract class AbstractModule 30{ 31 /** @var string The directory where the module is installed */ 32 private $directory; 33 34 /** @var string[] A cached copy of the module settings */ 35 private $settings; 36 37 /** @var string For custom modules - optional (recommended) version number */ 38 const CUSTOM_VERSION = ''; 39 40 /** @var string For custom modules - link for support, upgrades, etc. */ 41 const CUSTOM_WEBSITE = ''; 42 43 protected $layout = 'layouts/default'; 44 45 /** 46 * Create a new module. 47 * 48 * @param string $directory Where is this module installed 49 */ 50 public function __construct(string $directory) 51 { 52 $this->directory = $directory; 53 } 54 55 /** 56 * Get a block setting. 57 * 58 * @param int $block_id 59 * @param string $setting_name 60 * @param string $default_value 61 * 62 * @return string 63 */ 64 public function getBlockSetting(int $block_id, string $setting_name, string $default_value = ''): string 65 { 66 $setting_value = Database::prepare( 67 "SELECT setting_value FROM `##block_setting` WHERE block_id = :block_id AND setting_name = :setting_name" 68 )->execute([ 69 'block_id' => $block_id, 70 'setting_name' => $setting_name, 71 ])->fetchOne(); 72 73 return $setting_value ?? $default_value; 74 } 75 76 /** 77 * Set a block setting. 78 * 79 * @param int $block_id 80 * @param string $setting_name 81 * @param string $setting_value 82 * 83 * @return $this 84 */ 85 public function setBlockSetting(int $block_id, string $setting_name, string $setting_value): self 86 { 87 Database::prepare( 88 "REPLACE INTO `##block_setting` (block_id, setting_name, setting_value) VALUES (:block_id, :setting_name, :setting_value)" 89 )->execute([ 90 'block_id' => $block_id, 91 'setting_name' => $setting_name, 92 'setting_value' => $setting_value, 93 ]); 94 95 return $this; 96 } 97 98 /** 99 * What is the default access level for this module? 100 * 101 * Some modules are aimed at admins or managers, and are not generally shown to users. 102 * 103 * @return int 104 */ 105 public function defaultAccessLevel(): int 106 { 107 // Returns one of: Auth::PRIV_HIDE, Auth::PRIV_PRIVATE, Auth::PRIV_USER, WT_PRIV_ADMIN 108 return Auth::PRIV_PRIVATE; 109 } 110 111 /** 112 * Provide a unique internal name for this module 113 * 114 * @return string 115 */ 116 public function getName(): string 117 { 118 return basename($this->directory); 119 } 120 121 /** 122 * Load all the settings for the module into a cache. 123 * 124 * Since modules may have many settings, and will probably want to use 125 * lots of them, load them all at once and cache them. 126 * 127 * @return void 128 */ 129 private function loadAllSettings() 130 { 131 if ($this->settings === null) { 132 $this->settings = DB::table('module_setting') 133 ->where('module_name', '=', $this->getName()) 134 ->pluck('setting_value', 'setting_name') 135 ->all(); 136 } 137 } 138 139 /** 140 * Get a module setting. Return a default if the setting is not set. 141 * 142 * @param string $setting_name 143 * @param string $default 144 * 145 * @return string 146 */ 147 public function getPreference($setting_name, $default = '') 148 { 149 $this->loadAllSettings(); 150 151 if (array_key_exists($setting_name, $this->settings)) { 152 return $this->settings[$setting_name]; 153 } 154 155 return $default; 156 } 157 158 /** 159 * Set a module setting. 160 * 161 * Since module settings are NOT NULL, setting a value to NULL will cause 162 * it to be deleted. 163 * 164 * @param string $setting_name 165 * @param string $setting_value 166 * 167 * @return $this 168 */ 169 public function setPreference($setting_name, $setting_value): self 170 { 171 $this->loadAllSettings(); 172 173 if (!array_key_exists($setting_name, $this->settings)) { 174 Database::prepare( 175 "INSERT INTO `##module_setting` (module_name, setting_name, setting_value) VALUES (?, ?, ?)" 176 )->execute([ 177 $this->getName(), 178 $setting_name, 179 $setting_value, 180 ]); 181 182 $this->settings[$setting_name] = $setting_value; 183 } elseif ($setting_value !== $this->getPreference($setting_name)) { 184 Database::prepare( 185 "UPDATE `##module_setting` SET setting_value = ? WHERE module_name = ? AND setting_name = ?" 186 )->execute([ 187 $setting_value, 188 $this->getName(), 189 $setting_name, 190 ]); 191 192 $this->settings[$setting_name] = $setting_value; 193 } 194 195 return $this; 196 } 197 198 /** 199 * Get a the current access level for a module 200 * 201 * @param Tree $tree 202 * @param string $component tab, block, menu, etc 203 * 204 * @return int 205 */ 206 public function getAccessLevel(Tree $tree, $component) 207 { 208 $access_level = Database::prepare( 209 "SELECT access_level FROM `##module_privacy` WHERE gedcom_id = :gedcom_id AND module_name = :module_name AND component = :component" 210 )->execute([ 211 'gedcom_id' => $tree->id(), 212 'module_name' => $this->getName(), 213 'component' => $component, 214 ])->fetchOne(); 215 216 if ($access_level === null) { 217 return $this->defaultAccessLevel(); 218 } 219 220 return (int) $access_level; 221 } 222 223 /** 224 * Create a response object from a view. 225 * 226 * @param string $view_name 227 * @param mixed[] $view_data 228 * @param int $status 229 * 230 * @return Response 231 */ 232 protected function viewResponse($view_name, $view_data, $status = Response::HTTP_OK): Response 233 { 234 // Make the view's data available to the layout. 235 $layout_data = $view_data; 236 237 // Render the view 238 $layout_data['content'] = view($view_name, $view_data); 239 240 // Insert the view into the layout 241 $html = view($this->layout, $layout_data); 242 243 return new Response($html, $status); 244 } 245} 246