+/**
+ * @brief Subpage to modify the server wide block list via the admin panel.
+ *
+ * This function generates the subpage of the admin panel to allow the
+ * modification of the node wide block/black list to block entire
+ * remote servers from communication with this node. The page allows
+ * adding, removing and editing of entries from the blocklist.
+ *
+ * @param App $a
+ * @return string
+ */
+function admin_page_blocklist(App $a)
+{
+ $blocklist = Config::get('system', 'blocklist');
+ $blocklistform = array();
+ if (is_array($blocklist)) {
+ foreach ($blocklist as $id => $b) {
+ $blocklistform[] = array(
+ 'domain' => array("domain[$id]", t('Blocked domain'), $b['domain'], '', t('The blocked domain'), 'required', '', ''),
+ 'reason' => array("reason[$id]", t("Reason for the block"), $b['reason'], t('The reason why you blocked this domain.') . '(' . $b['domain'] . ')', 'required', '', ''),
+ 'delete' => array("delete[$id]", t("Delete domain") . ' (' . $b['domain'] . ')', False, t("Check to delete this entry from the blocklist"))
+ );
+ }
+ }
+ $t = get_markup_template('admin/blocklist.tpl');
+ return replace_macros($t, array(
+ '$title' => t('Administration'),
+ '$page' => t('Server Blocklist'),
+ '$intro' => t('This page can be used to define a black list of servers from the federated network that are not allowed to interact with your node. For all entered domains you should also give a reason why you have blocked the remote server.'),
+ '$public' => t('The list of blocked servers will be made publically available on the /friendica page so that your users and people investigating communication problems can find the reason easily.'),
+ '$addtitle' => t('Add new entry to block list'),
+ '$newdomain' => array('newentry_domain', t('Server Domain'), '', t('The domain of the new server to add to the block list. Do not include the protocol.'), 'required', '', ''),
+ '$newreason' => array('newentry_reason', t('Block reason'), '', t('The reason why you blocked this domain.'), 'required', '', ''),
+ '$submit' => t('Add Entry'),
+ '$savechanges' => t('Save changes to the blocklist'),
+ '$currenttitle' => t('Current Entries in the Blocklist'),
+ '$thurl' => t('Blocked domain'),
+ '$threason' => t('Reason for the block'),
+ '$delentry' => t('Delete entry from blocklist'),
+ '$entries' => $blocklistform,
+ '$baseurl' => System::baseUrl(true),
+ '$confirm_delete' => t('Delete entry from blocklist?'),
+ '$form_security_token' => get_form_security_token("admin_blocklist")
+ ));
+}
+
+/**
+ * @brief Process send data from Admin Blocklist Page
+ *
+ * @param App $a
+ */
+function admin_page_blocklist_post(App $a)
+{
+ if (!x($_POST, "page_blocklist_save") && (!x($_POST['page_blocklist_edit']))) {
+ return;
+ }
+
+ check_form_security_token_redirectOnErr('/admin/blocklist', 'admin_blocklist');
+
+ if (x($_POST['page_blocklist_save'])) {
+ // Add new item to blocklist
+ $blocklist = Config::get('system', 'blocklist');
+ $blocklist[] = array(
+ 'domain' => notags(trim($_POST['newentry_domain'])),
+ 'reason' => notags(trim($_POST['newentry_reason']))
+ );
+ Config::set('system', 'blocklist', $blocklist);
+ info(t('Server added to blocklist.') . EOL);
+ } else {
+ // Edit the entries from blocklist
+ $blocklist = array();
+ foreach ($_POST['domain'] as $id => $domain) {
+ // Trimming whitespaces as well as any lingering slashes
+ $domain = notags(trim($domain, "\x00..\x1F/"));
+ $reason = notags(trim($_POST['reason'][$id]));
+ if (!x($_POST['delete'][$id])) {
+ $blocklist[] = array(
+ 'domain' => $domain,
+ 'reason' => $reason
+ );
+ }
+ }
+ Config::set('system', 'blocklist', $blocklist);
+ info(t('Site blocklist updated.') . EOL);
+ }
+ goaway('admin/blocklist');
+
+ return; // NOTREACHED
+}
+
+/**
+ * @brief Process data send by the contact block admin page
+ *
+ * @param App $a
+ */
+function admin_page_contactblock_post(App $a)
+{
+ $contact_url = x($_POST, 'contact_url') ? $_POST['contact_url'] : '';
+ $contacts = x($_POST, 'contacts') ? $_POST['contacts'] : [];
+
+ check_form_security_token_redirectOnErr('/admin/contactblock', 'admin_contactblock');
+
+ if (x($_POST, 'page_contactblock_block')) {
+ $contact_id = Contact::getIdForURL($contact_url, 0);
+ if ($contact_id) {
+ Contact::block($contact_id);
+ notice(t('The contact has been blocked from the node'));
+ } else {
+ notice(t('Could not find any contact entry for this URL (%s)', $contact_url));
+ }
+ }
+ if (x($_POST, 'page_contactblock_unblock')) {
+ foreach ($contacts as $uid) {
+ Contact::unblock($uid);
+ }
+ notice(tt("%s contact unblocked", "%s contacts unblocked", count($contacts)));
+ }
+ goaway('admin/contactblock');
+ return; // NOTREACHED
+}
+
+/**
+ * @brief Admin panel for server-wide contact block
+ *
+ * @param App $a
+ * @return string
+ */
+function admin_page_contactblock(App $a)
+{
+ $condition = ['uid' => 0, 'blocked' => true];
+
+ $total = dba::count('contact', $condition);
+
+ $a->set_pager_total($total);
+ $a->set_pager_itemspage(30);
+
+ $statement = dba::select('contact', [], $condition, ['limit' => [$a->pager['start'], $a->pager['itemspage']]]);
+
+ $contacts = dba::inArray($statement);
+
+ $t = get_markup_template('admin/contactblock.tpl');
+ $o = replace_macros($t, array(
+ // strings //
+ '$title' => t('Administration'),
+ '$page' => t('Remote Contact Blocklist'),
+ '$description' => t('This page allows you to prevent any message from a remote contact to reach your node.'),
+ '$submit' => t('Block Remote Contact'),
+ '$select_all' => t('select all'),
+ '$select_none' => t('select none'),
+ '$block' => t('Block'),
+ '$unblock' => t('Unblock'),
+ '$no_data' => t('No remote contact is blocked from this node.'),
+
+ '$h_contacts' => t('Blocked Remote Contacts'),
+ '$h_newblock' => t('Block New Remote Contact'),
+ '$th_contacts' => [t('Photo'), t('Name'), t('Address'), t('Profile URL')],
+
+ '$form_security_token' => get_form_security_token("admin_contactblock"),
+
+ // values //
+ '$baseurl' => System::baseUrl(true),
+
+ '$contacts' => $contacts,
+ '$total_contacts' => tt('%s total blocked contact', '%s total blocked contacts', $total),
+ '$paginate' => paginate($a),
+ '$contacturl' => ['contact_url', t("Profile URL"), '', t("URL of the remote contact to block.")],
+ ));
+ return $o;
+}
+
+/**
+ * @brief Subpage where the admin can delete an item from their node given the GUID
+ *
+ * This subpage of the admin panel offers the nodes admin to delete an item from
+ * the node, given the GUID or the display URL such as http://example.com/display/123456.
+ * The item will then be marked as deleted in the database and processed accordingly.
+ *
+ * @param App $a
+ * @return string
+ */
+function admin_page_deleteitem(App $a)
+{
+ $t = get_markup_template('admin/deleteitem.tpl');
+
+ return replace_macros($t, array(
+ '$title' => t('Administration'),
+ '$page' => t('Delete Item'),
+ '$submit' => t('Delete this Item'),
+ '$intro1' => t('On this page you can delete an item from your node. If the item is a top level posting, the entire thread will be deleted.'),
+ '$intro2' => t('You need to know the GUID of the item. You can find it e.g. by looking at the display URL. The last part of http://example.com/display/123456 is the GUID, here 123456.'),
+ '$deleteitemguid' => array('deleteitemguid', t("GUID"), '', t("The GUID of the item you want to delete."), 'required', 'autofocus'),
+ '$baseurl' => System::baseUrl(),
+ '$form_security_token' => get_form_security_token("admin_deleteitem")
+ ));
+}
+
+/**
+ * @brief Process send data from Admin Delete Item Page
+ *
+ * The GUID passed through the form should be only the GUID. But we also parse
+ * URLs like the full /display URL to make the process more easy for the admin.
+ *
+ * @param App $a
+ */
+function admin_page_deleteitem_post(App $a)
+{
+ if (!x($_POST['page_deleteitem_submit'])) {
+ return;
+ }
+
+ check_form_security_token_redirectOnErr('/admin/deleteitem/', 'admin_deleteitem');
+
+ if (x($_POST['page_deleteitem_submit'])) {
+ $guid = trim(notags($_POST['deleteitemguid']));
+ // The GUID should not include a "/", so if there is one, we got an URL
+ // and the last part of it is most likely the GUID.
+ if (strpos($guid, '/')) {
+ $guid = substr($guid, strrpos($guid, '/') + 1);
+ }
+ // Now that we have the GUID get all IDs of the associated entries in the
+ // item table of the DB and drop those items, which will also delete the
+ // associated threads.
+ $r = dba::select('item', array('id'), array('guid' => $guid));
+ while ($row = dba::fetch($r)) {
+ drop_item($row['id'], false);
+ }
+ dba::close($r);
+ }
+
+ info(t('Item marked for deletion.') . EOL);
+ goaway('admin/deleteitem');
+ return; // NOTREACHED
+}
+