3 * @copyright Copyright (C) 2010-2023, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Module\Settings;
25 use Friendica\Content\Conversation\Factory\Timeline;
26 use Friendica\Content\Conversation\Repository\Channel;
27 use Friendica\Core\L10n;
28 use Friendica\Core\Renderer;
29 use Friendica\Core\Session\Capability\IHandleUserSessions;
30 use Friendica\Model\Circle;
31 use Friendica\Module\BaseSettings;
32 use Friendica\Module\Response;
33 use Friendica\Network\HTTPException;
34 use Friendica\Util\Profiler;
35 use Psr\Log\LoggerInterface;
37 class Channels extends BaseSettings
44 public function __construct(Timeline $timeline, Channel $channel, App\Page $page, IHandleUserSessions $session, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
46 parent::__construct($session, $page, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
48 $this->timeline = $timeline;
49 $this->channel = $channel;
52 protected function post(array $request = [])
54 $uid = $this->session->getLocalUserId();
56 throw new HTTPException\ForbiddenException($this->t('Permission denied.'));
59 if (empty($request['edit_channel']) && empty($request['add_channel'])) {
63 self::checkFormSecurityTokenRedirectOnError('/settings/channels', 'settings_channels');
65 if (!empty($request['add_channel'])) {
66 $channel = $this->timeline->createFromTableRow([
67 'label' => $request['new_label'],
68 'description' => $request['new_description'],
69 'access-key' => substr(mb_strtolower($request['new_access_key']), 0, 1),
71 'circle' => (int)$request['new_circle'],
72 'include-tags' => $this->cleanTags($request['new_include_tags']),
73 'exclude-tags' => $this->cleanTags($request['new_exclude_tags']),
74 'full-text-search' => $this->cleanTags($request['new_text_search']),
75 'media-type' => ($request['new_image'] ? 1 : 0) | ($request['new_video'] ? 2 : 0) | ($request['new_audio'] ? 4 : 0),
77 $saved = $this->channel->save($channel);
78 $this->logger->debug('New channel added', ['saved' => $saved]);
82 foreach (array_keys($request['label']) as $id) {
83 if ($request['delete'][$id]) {
84 $success = $this->channel->deleteById($id, $uid);
85 $this->logger->debug('Channel deleted', ['id' => $id, 'success' => $success]);
89 $channel = $this->timeline->createFromTableRow([
91 'label' => $request['label'][$id],
92 'description' => $request['description'][$id],
93 'access-key' => substr(mb_strtolower($request['access_key'][$id]), 0, 1),
95 'circle' => (int)$request['circle'][$id],
96 'include-tags' => $this->cleanTags($request['include_tags'][$id]),
97 'exclude-tags' => $this->cleanTags($request['exclude_tags'][$id]),
98 'full-text-search' => $this->cleanTags($request['text_search'][$id]),
99 'media-type' => ($request['image'][$id] ? 1 : 0) | ($request['video'][$id] ? 2 : 0) | ($request['audio'][$id] ? 4 : 0),
101 $saved = $this->channel->save($channel);
102 $this->logger->debug('Save channel', ['id' => $id, 'saved' => $saved]);
105 $this->baseUrl->redirect('/settings/channels');
108 protected function content(array $request = []): string
112 $uid = $this->session->getLocalUserId();
114 throw new HTTPException\ForbiddenException($this->t('Permission denied.'));
118 0 => $this->l10n->t('Global Community'),
119 -1 => $this->l10n->t('Following'),
120 -2 => $this->l10n->t('Followers'),
123 foreach (Circle::getByUserId($uid) as $circle) {
124 $circles[$circle['id']] = $circle['name'];
128 foreach ($this->channel->selectByUid($uid) as $channel) {
130 'label' => ["label[$channel->code]", $this->t('Label'), $channel->label, '', $this->t('Required')],
131 'description' => ["description[$channel->code]", $this->t("Description"), $channel->description],
132 'access_key' => ["access_key[$channel->code]", $this->t("Access Key"), $channel->accessKey],
133 'circle' => ["circle[$channel->code]", $this->t('Circle/Channel'), $channel->circle, '', $circles],
134 'include_tags' => ["include_tags[$channel->code]", $this->t("Include Tags"), $channel->includeTags],
135 'exclude_tags' => ["exclude_tags[$channel->code]", $this->t("Exclude Tags"), $channel->excludeTags],
136 'text_search' => ["text_search[$channel->code]", $this->t("Full Text Search"), $channel->fullTextSearch],
137 'image' => ["image[$channel->code]", $this->t("Images"), $channel->mediaType & 1],
138 'video' => ["video[$channel->code]", $this->t("Videos"), $channel->mediaType & 2],
139 'audio' => ["audio[$channel->code]", $this->t("Audio"), $channel->mediaType & 4],
140 'delete' => ["delete[$channel->code]", $this->t("Delete channel") . ' (' . $channel->label . ')', false, $this->t("Check to delete this entry from the channel list")]
144 $t = Renderer::getMarkupTemplate('settings/channels.tpl');
145 return Renderer::replaceMacros($t, [
146 'label' => ["new_label", $this->t('Label'), '', $this->t('Short name for the channel. It is displayed on the channels widget.'), $this->t('Required')],
147 'description' => ["new_description", $this->t("Description"), '', $this->t('This should describe the content of the channel in a few word.')],
148 'access_key' => ["new_access_key", $this->t("Access Key"), '', $this->t('When you want to access this channel via an access key, you can define it here. Pay attention to not use an already used one.')],
149 'circle' => ['new_circle', $this->t('Circle/Channel'), 0, $this->t('Select a circle or channel, that your channel should be based on.'), $circles],
150 'include_tags' => ["new_include_tags", $this->t("Include Tags"), '', $this->t('Comma separated list of tags. A post will be used when it contains any of the listed tags.')],
151 'exclude_tags' => ["new_exclude_tags", $this->t("Exclude Tags"), '', $this->t('Comma separated list of tags. If a post contain any of these tags, then it will not be part of nthis channel.')],
152 'text_search' => ["new_text_search", $this->t("Full Text Search"), '', $this->t('Search terms for the body, supports the "boolean mode" operators from MariaDB. See the help for a complete list of operators and additional keywords: %s', '<a href="help/Channels">help/Channels</a>')],
153 'image' => ['new_image', $this->t("Images"), false, $this->t("Check to display images in the channel.")],
154 'video' => ["new_video", $this->t("Videos"), false, $this->t("Check to display videos in the channel.")],
155 'audio' => ["new_audio", $this->t("Audio"), false, $this->t("Check to display audio in the channel.")],
157 'title' => $this->t('Channels'),
158 'intro' => $this->t('This page can be used to define your own channels.'),
159 'addtitle' => $this->t('Add new entry to the channel list'),
160 'addsubmit' => $this->t('Add'),
161 'savechanges' => $this->t('Save'),
162 'currenttitle' => $this->t('Current Entries in the channel list'),
163 'thurl' => $this->t('Blocked server domain pattern'),
164 'threason' => $this->t('Reason for the block'),
165 'delentry' => $this->t('Delete entry from the channel list'),
166 'confirm_delete' => $this->t('Delete entry from the channel list?'),
168 '$entries' => $blocklistform,
169 '$baseurl' => $this->baseUrl,
171 '$form_security_token' => self::getFormSecurityToken('settings_channels'),
175 private function cleanTags(string $tag_list): string
179 $tagitems = explode(',', mb_strtolower($tag_list));
180 foreach ($tagitems as $tag) {
181 $tag = trim($tag, '# ');
186 return implode(',', $tags);