Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Configurator.php
001 <?php
002
003 /**
004 * @package s9e\TextFormatter
005 * @copyright Copyright (c) 2010-2022 The s9e authors
006 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
007 */
008 namespace s9e\TextFormatter\Plugins\Censor;
009
010 use ArrayAccess;
011 use Countable;
012 use Iterator;
013 use s9e\TextFormatter\Configurator\Collections\NormalizedCollection;
014 use s9e\TextFormatter\Configurator\Helpers\ConfigHelper;
015 use s9e\TextFormatter\Configurator\Helpers\RegexpBuilder;
016 use s9e\TextFormatter\Configurator\Items\Regexp;
017 use s9e\TextFormatter\Configurator\JavaScript\Code;
018 use s9e\TextFormatter\Configurator\JavaScript\RegexpConvertor;
019 use s9e\TextFormatter\Configurator\Traits\CollectionProxy;
020 use s9e\TextFormatter\Plugins\ConfiguratorBase;
021
022 /**
023 * @method mixed add(string $key, mixed $value) Add an item to this collection
024 * @method array asConfig()
025 * @method void clear() Empty this collection
026 * @method bool contains(mixed $value) Test whether a given value is present in this collection
027 * @method integer count()
028 * @method mixed current()
029 * @method void delete(string $key) Delete an item from this collection
030 * @method bool exists(string $key) Test whether an item of given key exists
031 * @method mixed get(string $key) Return a value from this collection
032 * @method mixed indexOf(mixed $value) Find the index of a given value
033 * @method integer|string key()
034 * @method mixed next()
035 * @method string normalizeKey(string $key) Normalize an item's key
036 * @method mixed normalizeValue(mixed $value) Normalize a value for storage
037 * @method bool offsetExists(string|integer $offset)
038 * @method mixed offsetGet(string|integer $offset)
039 * @method void offsetSet(string|integer $offset, mixed $value)
040 * @method void offsetUnset(string|integer $offset)
041 * @method string onDuplicate(string|null $action) Query and set the action to take when add() is called with a key that already exists
042 * @method void rewind()
043 * @method mixed set(string $key, mixed $value) Set and overwrite a value in this collection
044 * @method bool valid()
045 */
046 class Configurator extends ConfiguratorBase implements ArrayAccess, Countable, Iterator
047 {
048 use CollectionProxy;
049
050 /**
051 * @var array List of whitelisted words as [word => true]
052 */
053 protected $allowed = [];
054
055 /**
056 * @var string Name of attribute used for the replacement
057 */
058 protected $attrName = 'with';
059
060 /**
061 * @var NormalizedCollection List of [word => replacement]
062 */
063 protected $collection;
064
065 /**
066 * @var string Default string used to replace censored words
067 */
068 protected $defaultReplacement = '****';
069
070 /**
071 * @var array Options passed to the RegexpBuilder
072 */
073 protected $regexpOptions = [
074 'caseInsensitive' => true,
075 'specialChars' => [
076 '*' => '[\\pL\\pN]*',
077 '?' => '.',
078 ' ' => '\\s*'
079 ]
080 ];
081
082 /**
083 * @var string Name of the tag used to mark censored words
084 */
085 protected $tagName = 'CENSOR';
086
087 /**
088 * Plugin's setup
089 *
090 * Will initialize its collection and create the plugin's tag if it does not exist
091 */
092 protected function setUp()
093 {
094 $this->collection = new NormalizedCollection;
095 $this->collection->onDuplicate('replace');
096
097 if (isset($this->configurator->tags[$this->tagName]))
098 {
099 return;
100 }
101
102 // Create a tag
103 $tag = $this->configurator->tags->add($this->tagName);
104
105 // Create the attribute and make it optional
106 $tag->attributes->add($this->attrName)->required = false;
107
108 // Ensure that censored content can't ever be used by other tags
109 $tag->rules->ignoreTags();
110
111 // Create a template that renders censored words either as their custom replacement or as
112 // the default replacement
113 $tag->template =
114 '<xsl:choose>
115 <xsl:when test="@' . $this->attrName . '">
116 <xsl:value-of select="@' . htmlspecialchars($this->attrName) . '"/>
117 </xsl:when>
118 <xsl:otherwise>' . htmlspecialchars($this->defaultReplacement, ENT_COMPAT) . '</xsl:otherwise>
119 </xsl:choose>';
120 }
121
122 /**
123 * Add a word to the list of uncensored words
124 *
125 * @param string $word Word to exclude from the censored list
126 * @return void
127 */
128 public function allow($word)
129 {
130 $this->allowed[$word] = true;
131 }
132
133 /**
134 * Return an instance of s9e\TextFormatter\Plugins\Censor\Helper
135 *
136 * @return Helper
137 */
138 public function getHelper()
139 {
140 $config = $this->asConfig();
141 if (isset($config))
142 {
143 $config = ConfigHelper::filterConfig($config, 'PHP');
144 }
145 else
146 {
147 // Use a dummy config with a regexp that doesn't match anything
148 $config = [
149 'attrName' => $this->attrName,
150 'regexp' => '/(?!)/',
151 'tagName' => $this->tagName
152 ];
153 }
154
155 return new Helper($config);
156 }
157
158 /**
159 * {@inheritdoc}
160 */
161 public function asConfig()
162 {
163 $words = $this->getWords();
164
165 if (empty($words))
166 {
167 return;
168 }
169
170 // Create the config
171 $config = [
172 'attrName' => $this->attrName,
173 'regexp' => $this->getWordsRegexp(array_keys($words)),
174 'regexpHtml' => $this->getWordsRegexp(array_map('htmlspecialchars', array_keys($words))),
175 'tagName' => $this->tagName
176 ];
177
178 // Add custom replacements
179 $replacementWords = [];
180 foreach ($words as $word => $replacement)
181 {
182 if (isset($replacement) && $replacement !== $this->defaultReplacement)
183 {
184 $replacementWords[$replacement][] = $word;
185 }
186 }
187
188 foreach ($replacementWords as $replacement => $words)
189 {
190 $wordsRegexp = '/^' . RegexpBuilder::fromList($words, $this->regexpOptions) . '$/Diu';
191
192 $regexp = new Regexp($wordsRegexp);
193 $regexp->setJS(RegexpConvertor::toJS(str_replace('[\\pL\\pN]', '[^\\s!-\\/:-?]', $wordsRegexp)));
194
195 $config['replacements'][] = [$regexp, $replacement];
196 }
197
198 // Add the whitelist
199 if (!empty($this->allowed))
200 {
201 $config['allowed'] = $this->getWordsRegexp(array_keys($this->allowed));
202 }
203
204 return $config;
205 }
206
207 /**
208 * {@inheritdoc}
209 */
210 public function getJSHints()
211 {
212 $hints = [
213 'CENSOR_HAS_ALLOWED' => !empty($this->allowed),
214 'CENSOR_HAS_REPLACEMENTS' => false
215 ];
216 foreach ($this->getWords() as $replacement)
217 {
218 if (isset($replacement) && $replacement !== $this->defaultReplacement)
219 {
220 $hints['CENSOR_HAS_REPLACEMENTS'] = true;
221 break;
222 }
223 }
224
225 return $hints;
226 }
227
228 /**
229 * Return a list of censored words
230 *
231 * @return string[]
232 */
233 protected function getWords()
234 {
235 return array_diff_key(iterator_to_array($this->collection), $this->allowed);
236 }
237
238 /**
239 * Generate a regexp that matches the given list of words
240 *
241 * @param array $words List of words
242 * @return Regexp Regexp instance with a Unicode-free JS variant
243 */
244 protected function getWordsRegexp(array $words)
245 {
246 $expr = RegexpBuilder::fromList($words, $this->regexpOptions);
247 $regexp = new Regexp('/(?<![\\pL\\pN])' . $expr . '(?![\\pL\\pN])/Siu');
248
249 // JavaScript regexps don't support Unicode properties, so instead of Unicode letters
250 // we'll accept any non-whitespace, non-common punctuation
251 $expr = str_replace('[\\pL\\pN]', '[^\\s!-\\/:-?]', $expr);
252 $expr = str_replace('(?>', '(?:', $expr);
253 $regexp->setJS('/(?:^|\\W)' . $expr . '(?!\\w)/gi');
254
255 return $regexp;
256 }
257 }