1<?php
2/**
3 * Adds a record in the database, handles the preview and checks for missing
4 * category entries.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * @package phpMyFAQ
11 * @author Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2003-2020 phpMyFAQ Team
13 * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link https://www.phpmyfaq.de
15 * @since 2003-02-23
16 */
17
18use Abraham\TwitterOAuth\TwitterOAuth;
19use phpMyFAQ\Category;
20use phpMyFAQ\Changelog;
21use phpMyFAQ\Filter;
22use phpMyFAQ\Helper\LinkVerifierHelper;
23use phpMyFAQ\Instance\Elasticsearch;
24use phpMyFAQ\Link;
25use phpMyFAQ\Logging;
26use phpMyFAQ\Notification;
27use phpMyFAQ\Question;
28use phpMyFAQ\Services\Twitter;
29use phpMyFAQ\Strings;
30use phpMyFAQ\Tags;
31use phpMyFAQ\Visits;
32
33if (!defined('IS_VALID_PHPMYFAQ')) {
34    http_response_code(400);
35    exit();
36}
37
38if ($user->perm->checkRight($user->getUserId(), 'edit_faq') || $user->perm->checkRight($user->getUserId(), 'add_faq')) {
39
40    // FAQ data
41    $dateStart = Filter::filterInput(INPUT_POST, 'dateStart', FILTER_SANITIZE_STRING);
42    $dateEnd = Filter::filterInput(INPUT_POST, 'dateEnd', FILTER_SANITIZE_STRING);
43    $question = Filter::filterInput(INPUT_POST, 'question', FILTER_SANITIZE_STRING);
44    $categories = Filter::filterInputArray(
45        INPUT_POST,
46        [
47            'rubrik' => [
48                'filter' => FILTER_VALIDATE_INT,
49                'flags' => FILTER_REQUIRE_ARRAY,
50            ],
51        ]
52    );
53    $recordLang = Filter::filterInput(INPUT_POST, 'lang', FILTER_SANITIZE_STRING);
54    $tags = Filter::filterInput(INPUT_POST, 'tags', FILTER_SANITIZE_STRING);
55    $active = Filter::filterInput(INPUT_POST, 'active', FILTER_SANITIZE_STRING);
56    $sticky = Filter::filterInput(INPUT_POST, 'sticky', FILTER_SANITIZE_STRING);
57    if ($faqConfig->get('main.enableMarkdownEditor')) {
58        $content = Filter::filterInput(INPUT_POST, 'answer', FILTER_UNSAFE_RAW);
59    } else {
60        $content = Filter::filterInput(INPUT_POST, 'answer', FILTER_SANITIZE_SPECIAL_CHARS);
61    }
62    $keywords = Filter::filterInput(INPUT_POST, 'keywords', FILTER_SANITIZE_STRING);
63    $author = Filter::filterInput(INPUT_POST, 'author', FILTER_SANITIZE_STRING);
64    $email = Filter::filterInput(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
65    $comment = Filter::filterInput(INPUT_POST, 'comment', FILTER_SANITIZE_STRING);
66    $recordId = Filter::filterInput(INPUT_POST, 'id', FILTER_VALIDATE_INT);
67    $solutionId = Filter::filterInput(INPUT_POST, 'solution_id', FILTER_VALIDATE_INT);
68    $revisionId = Filter::filterInput(INPUT_POST, 'revision_id', FILTER_VALIDATE_INT);
69    $changed = Filter::filterInput(INPUT_POST, 'changed', FILTER_SANITIZE_STRING);
70    $date = Filter::filterInput(INPUT_POST, 'date', FILTER_SANITIZE_STRING);
71    $notes = Filter::filterInput(INPUT_POST, 'notes', FILTER_SANITIZE_STRING);
72
73    // Permissions
74    $permissions = [];
75    if ('all' === Filter::filterInput(INPUT_POST, 'userpermission', FILTER_SANITIZE_STRING)) {
76        $permissions += [
77            'restricted_user' => [
78                -1,
79            ],
80        ];
81    } else {
82        $permissions += [
83            'restricted_user' => [
84                Filter::filterInput(INPUT_POST, 'restricted_users', FILTER_VALIDATE_INT),
85            ],
86        ];
87    }
88
89    if ('all' === Filter::filterInput(INPUT_POST, 'grouppermission', FILTER_SANITIZE_STRING)) {
90        $permissions += [
91            'restricted_groups' => [
92                -1,
93            ],
94        ];
95    } else {
96        $permissions += Filter::filterInputArray(
97            INPUT_POST,
98            [
99                'restricted_groups' => [
100                    'filter' => FILTER_VALIDATE_INT,
101                    'flags' => FILTER_REQUIRE_ARRAY,
102                ],
103            ]
104        );
105    }
106
107    if (!isset($categories['rubrik'])) {
108        $categories['rubrik'] = [];
109    }
110
111    if (!is_null($question) && !is_null($categories['rubrik'])) {
112        // new entry
113        $logging = new Logging($faqConfig);
114        $logging->logAdmin($user, 'admin-save-new-faq');
115        printf(
116            '<header class="row"><div class="col-lg-12"><h2 class="page-header">%s</h2></div></header>',
117            $PMF_LANG['ad_entry_aor']
118        );
119
120        $category = new Category($faqConfig, [], false);
121        $category->setUser($currentAdminUser);
122        $category->setGroups($currentAdminGroups);
123        $tagging = new Tags($faqConfig);
124
125        $recordData = [
126            'lang' => $recordLang,
127            'active' => $active,
128            'sticky' => (!is_null($sticky) ? 1 : 0),
129            'thema' => Filter::removeAttributes(html_entity_decode($question, ENT_QUOTES | ENT_HTML5, 'UTF-8')),
130            'content' => Filter::removeAttributes(html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8')),
131            'keywords' => $keywords,
132            'author' => $author,
133            'email' => $email,
134            'comment' => (!is_null($comment) ? 'y' : 'n'),
135            'date' => empty($date) ? date('YmdHis') : str_replace(['-', ':', ' '], '', $date),
136            'dateStart' => (empty($dateStart) ? '00000000000000' : str_replace('-', '', $dateStart) . '000000'),
137            'dateEnd' => (empty($dateEnd) ? '99991231235959' : str_replace('-', '', $dateEnd) . '235959'),
138            'linkState' => '',
139            'linkDateCheck' => 0,
140            'notes' => Filter::removeAttributes($notes)
141        ];
142
143        // Add new record and get that ID
144        $recordId = $faq->addRecord($recordData);
145
146        if ($recordId) {
147            // Create ChangeLog entry
148            $changelog = new Changelog($faqConfig);
149            $changelog->addEntry($recordId, $user->getUserId(), nl2br($changed), $recordData['lang']);
150            // Create the visit entry
151
152            $visits = new Visits($faqConfig);
153            $visits->logViews($recordId);
154
155            // Insert the new category relations
156            $faq->addCategoryRelations($categories['rubrik'], $recordId, $recordData['lang']);
157            // Insert the tags
158            if ($tags != '') {
159                $tagging->saveTags($recordId, explode(',', trim($tags)));
160            }
161
162            // Add user permissions
163            $faq->addPermission('user', $recordId, $permissions['restricted_user']);
164            $category->addPermission('user', $categories['rubrik'], $permissions['restricted_user']);
165            // Add group permission
166            if ($faqConfig->get('security.permLevel') !== 'basic') {
167                $faq->addPermission('group', $recordId, $permissions['restricted_groups']);
168                $category->addPermission('group', $categories['rubrik'], $permissions['restricted_groups']);
169            }
170
171            // Open question answered
172            $questionObject = new Question($faqConfig);
173            $openQuestionId = Filter::filterInput(INPUT_POST, 'openQuestionId', FILTER_VALIDATE_INT);
174            if (0 !== $openQuestionId) {
175                if ($faqConfig->get('records.enableDeleteQuestion')) { // deletes question
176                    $questionObject->deleteQuestion($openQuestionId);
177                } else { // adds this faq record id to the related open question
178                    $questionObject->updateQuestionAnswer($openQuestionId, $recordId, $categories['rubrik'][0]);
179                }
180
181                $url = sprintf(
182                    '%s?action=faq&cat=%d&id=%d&artlang=%s',
183                    $faqConfig->getDefaultUrl(),
184                    $categories['rubrik'][0],
185                    $recordId,
186                    $recordLang
187                );
188                $oLink = new Link($url, $faqConfig);
189
190                // notify the user who added the question
191                $notifyEmail = Filter::filterInput(INPUT_POST, 'notifyEmail', FILTER_SANITIZE_EMAIL);
192                $notifyUser = Filter::filterInput(INPUT_POST, 'notifyUser', FILTER_SANITIZE_STRING);
193
194                $notification = new Notification($faqConfig);
195                $notification->sendOpenQuestionAnswered($notifyEmail, $notifyUser, $oLink->toString());
196            }
197
198            // Call Link Verification
199            LinkVerifierHelper::linkOndemandJavascript($recordId, $recordData['lang']);
200
201            // If Elasticsearch is enabled, index new FAQ document
202            if ($faqConfig->get('search.enableElasticsearch')) {
203                $esInstance = new Elasticsearch($faqConfig);
204                $esInstance->index(
205                    [
206                        'id' => $recordId,
207                        'lang' => $recordLang,
208                        'solution_id' => $solutionId,
209                        'question' => $recordData['thema'],
210                        'answer' => $recordData['content'],
211                        'keywords' => $keywords,
212                        'category_id' => $categories['rubrik'][0]
213                    ]
214                );
215            }
216
217            // Callback to Twitter if enabled
218            if ($faqConfig->get('socialnetworks.enableTwitterSupport')) {
219
220                $connection = new TwitterOAuth(
221                    $faqConfig->get('socialnetworks.twitterConsumerKey'),
222                    $faqConfig->get('socialnetworks.twitterConsumerSecret'),
223                    $faqConfig->get('socialnetworks.twitterAccessTokenKey'),
224                    $faqConfig->get('socialnetworks.twitterAccessTokenSecret')
225                );
226
227                $link = sprintf(
228                    'index.php?action=faq&amp;cat=%d&amp;id=%d&amp;artlang=%s',
229                    $categories['rubrik'][0],
230                    $recordId,
231                    $recordLang
232                );
233                $oLink = new Link($faqConfig->getDefaultUrl() . $link, $faqConfig);
234                $oLink->itemTitle = $question;
235                $link = $oLink->toString();
236
237                if ($connection) {
238                    $twitter = new Twitter($connection);
239                    $twitter->addPost($question, $tags, $link);
240                }
241            }
242
243            printf('<p class="alert alert-success">%s</p>', $PMF_LANG['ad_entry_savedsuc']);
244            ?>
245          <script>
246            (() => {
247              setTimeout(() => {
248                window.location = "index.php?action=editentry&id=<?= $recordId;
249                    ?>&lang=<?= $recordData['lang'] ?>";
250              }, 5000);
251            })();
252          </script>
253            <?php
254        } else {
255            printf(
256                '<p class="alert alert-danger">%s</p>',
257                $PMF_LANG['ad_entry_savedfail'] . $faqConfig->getDb()->error()
258            );
259        }
260    } else {
261        printf(
262            '<header class="row"><div class="col-lg-12"><h2 class="page-header"><i aria-hidden="true" class="fa fa-pencil"></i> %s</h2></div></header>',
263            $PMF_LANG['ad_entry_aor']
264        );
265        printf(
266            '<p class="alert alert-danger">%s</p>', $PMF_LANG['ad_entryins_fail']
267        );
268        ?>
269      <form action="?action=editpreview" method="post">
270        <input type="hidden" name="question" value="<?= Strings::htmlspecialchars($question) ?>">
271        <input type="hidden" name="content" class="mceNoEditor" value="<?= Strings::htmlspecialchars($content) ?>">
272        <input type="hidden" name="lang" value="<?= $recordLang ?>">
273        <input type="hidden" name="keywords" value="<?= $keywords ?>">
274        <input type="hidden" name="tags" value="<?= $tags ?>">
275        <input type="hidden" name="author" value="<?= $author ?>">
276        <input type="hidden" name="email" value="<?= $email ?>">
277          <?php
278          if (is_array($categories['rubrik'])) {
279              foreach ($categories['rubrik'] as $key => $_categories) {
280                  echo '    <input type="hidden" name="rubrik[' . $key . ']" value="' . $_categories . '" />';
281              }
282          }
283          ?>
284        <input type="hidden" name="solution_id" value="<?= $solutionId ?>">
285        <input type="hidden" name="revision" value="<?= $revisionId ?>">
286        <input type="hidden" name="active" value="<?= $active ?>">
287        <input type="hidden" name="changed" value="<?= $changed ?>">
288        <input type="hidden" name="comment" value="<?= $comment ?>">
289        <input type="hidden" name="dateStart" value="<?= $dateStart ?>">
290        <input type="hidden" name="dateEnd" value="<?= $dateEnd ?>">
291        <input type="hidden" name="userpermission" value="<?= $user_permission ?>">
292        <input type="hidden" name="restricted_users" value="<?= $permissions['restricted_user'] ?>">
293        <input type="hidden" name="grouppermission" value="<?= $group_permission ?>">
294        <input type="hidden" name="restricted_group" value="<?= $permissions['restricted_groups'] ?>">
295        <input type="hidden" name="date" value="<?= $date ?>">
296        <input type="hidden" name="notes" value="<?= $notes ?>">
297        <p class="text-center">
298          <button class="btn btn-primary" type="submit" name="submit">
299              <?= $PMF_LANG['ad_entry_back'] ?>
300          </button>
301        </p>
302      </form>
303        <?php
304    }
305} else {
306    echo $PMF_LANG['err_NotAuth'];
307}
308