Merge pull request 'Proxify functionality is removed' (#1495) from heluecht/friendica...
[friendica-addons.git/.git] / nsfw / nsfw.php
1 <?php
2
3 /**
4  * Name: NSFW
5  * Description: Collapse posts with inappropriate content
6  * Version: 1.0
7  * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
8  *
9  */
10
11 use Friendica\App;
12 use Friendica\Core\Hook;
13 use Friendica\Core\Renderer;
14 use Friendica\DI;
15
16 function nsfw_install()
17 {
18         Hook::register('prepare_body_content_filter', 'addon/nsfw/nsfw.php', 'nsfw_prepare_body_content_filter', 10);
19         Hook::register('addon_settings', 'addon/nsfw/nsfw.php', 'nsfw_addon_settings');
20         Hook::register('addon_settings_post', 'addon/nsfw/nsfw.php', 'nsfw_addon_settings_post');
21 }
22
23 // This function isn't perfect and isn't trying to preserve the html structure - it's just a
24 // quick and dirty filter to pull out embedded photo blobs because 'nsfw' seems to come up
25 // inside them quite often. We don't need anything fancy, just pull out the data blob so we can
26 // check against the rest of the body.
27
28 function nsfw_extract_photos($body)
29 {
30         $new_body = '';
31
32         $img_start = strpos($body, 'src="data:');
33         $img_end = (($img_start !== false) ? strpos(substr($body, $img_start), '>') : false);
34
35         $cnt = 0;
36
37         while ($img_end !== false) {
38                 $img_end += $img_start;
39                 $new_body = $new_body . substr($body, 0, $img_start);
40
41                 $cnt ++;
42                 $body = substr($body, 0, $img_end);
43
44                 $img_start = strpos($body, 'src="data:');
45                 $img_end = (($img_start !== false) ? strpos(substr($body, $img_start), '>') : false);
46         }
47
48         if (!$cnt) {
49                 return $body;
50         }
51         return $new_body;
52 }
53
54 function nsfw_addon_settings(array &$data)
55 {
56         if (!DI::userSession()->getLocalUserId()) {
57                 return;
58         }
59
60         $enabled = !DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'nsfw', 'disable');
61         $words   = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'nsfw', 'words', 'nsfw,');
62
63         $t    = Renderer::getMarkupTemplate('settings.tpl', 'addon/nsfw/');
64         $html = Renderer::replaceMacros($t, [
65                 '$info'    => DI::l10n()->t('This addon searches for specified words/text in posts and collapses them. It can be used to filter content tagged with for instance #NSFW that may be deemed inappropriate at certain times or places, such as being at work. It is also useful for hiding irrelevant or annoying content from direct view.'),
66                 '$enabled' => ['nsfw-enable', DI::l10n()->t('Enable Content filter'), $enabled],
67                 '$words'   => ['nsfw-words', DI::l10n()->t('Comma separated list of keywords to hide'), $words, DI::l10n()->t('Use /expression/ to provide regular expressions, #tag to specfically match hashtags (case-insensitive), or regular words (case-sensitive)')],
68         ]);
69
70         $data = [
71                 'addon' => 'nsfw',
72                 'title' => DI::l10n()->t('Content Filter (NSFW and more)'),
73                 'html'  => $html,
74         ];
75 }
76
77 function nsfw_addon_settings_post(array &$b)
78 {
79         if (!DI::userSession()->getLocalUserId()) {
80                 return;
81         }
82
83         if (!empty($_POST['nsfw-submit'])) {
84                 $enable = !empty($_POST['nsfw-enable']) ? intval($_POST['nsfw-enable']) : 0;
85                 $disable = 1 - $enable;
86
87                 $words = trim($_POST['nsfw-words']);
88                 $word_list = explode(',', $words);
89                 foreach ($word_list as $word) {
90                         $word = trim($word);
91                         if (!$words || strpos($word, '/') !== 0) {
92                                 continue;
93                         }
94
95                         if (@preg_match($word, '') === false) {
96                                 DI::sysmsg()->addNotice(DI::l10n()->t('Regular expression "%s" fails to compile', $word));
97                         };
98                 }
99
100                 DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'nsfw', 'words', $words);
101                 DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'nsfw', 'disable', $disable);
102         }
103 }
104
105 function nsfw_prepare_body_content_filter(&$hook_data)
106 {
107         $words = null;
108         if (DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'nsfw', 'disable')) {
109                 return;
110         }
111
112         if (DI::userSession()->getLocalUserId()) {
113                 $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'nsfw', 'words');
114         }
115
116         if ($words) {
117                 $word_list = explode(',', $words);
118         } else {
119                 $word_list = ['nsfw'];
120         }
121
122         $found = false;
123         if (count($word_list)) {
124                 $body = $hook_data['item']['title'] . "\n" . nsfw_extract_photos($hook_data['item']['body']);
125
126                 foreach ($word_list as $word) {
127                         $word = trim($word);
128                         if (!strlen($word)) {
129                                 continue;
130                         }
131
132                         $tag_search = false;
133                         switch ($word[0]) {
134                                 case '/'; // Regular expression
135                                         $found = @preg_match($word, $body);
136                                         break;
137                                 case '#': // Hashtag-only search
138                                         $tag_search = true;
139                                         $found = nsfw_find_word_in_item_tags($hook_data['item']['hashtags'], substr($word, 1));
140                                         break;
141                                 default:
142                                         $found = strpos($body, $word) !== false || nsfw_find_word_in_item_tags($hook_data['item']['tags'], $word);
143                                         break;
144                         }
145
146                         if ($found) {
147                                 break;
148                         }
149                 }
150         }
151
152         if ($found) {
153                 if ($tag_search) {
154                         $hook_data['filter_reasons'][] = DI::l10n()->t('Filtered tag: %s', $word);
155                 } else {
156                         $hook_data['filter_reasons'][] = DI::l10n()->t('Filtered word: %s', $word);
157                 }
158         }
159 }
160
161 function nsfw_find_word_in_item_tags($item_tags, $word)
162 {
163         if (is_array($item_tags)) {
164                 foreach ($item_tags as $tag) {
165                         if (stripos($tag, '>' . $word . '<') !== false) {
166                                 return true;
167                         }
168                 }
169         }
170
171         return false;
172 }