From a338c75c9fe7c1d45d4c7274fafcb59bdc4c3764 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Wed, 1 May 2019 19:46:18 +0200 Subject: [PATCH 1/5] Update README for Modules --- src/Module/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Module/README.md b/src/Module/README.md index f888f2632..66a4967f3 100644 --- a/src/Module/README.md +++ b/src/Module/README.md @@ -3,6 +3,21 @@ The Module namespace contains the different modules of Friendica. Each module is loaded through the [`App`](https://github.com/friendica/friendica/blob/develop/src/App.php). +There are mainly two types of modules: +- frontend modules to interact with users +- backend modules to interact with machine requests + +### Frontend modules + +This type of modules mainly needs a template, which are generally located at +[view/templates/](https://github.com/friendica/friendica/tree/develop/view/templates). + +A frontend module should extend the [`BaseModule`](https://github.com/friendica/friendica/blob/develop/src/BaseModule.php), especially the `content()` method. + +### Backend modules + +This type of modules mainly responds either with `XML` or with `JSON`. + Rules for Modules: - Named like the call (i.e. https://friendica.test/contact => `Contact`) - Start with capitals and are **not** camelCased. From b3cee63745572e689490d221f8eb80438b87373b Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Wed, 1 May 2019 19:58:51 +0200 Subject: [PATCH 2/5] Update README for Modules --- src/Module/README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Module/README.md b/src/Module/README.md index 66a4967f3..a2f2b9de5 100644 --- a/src/Module/README.md +++ b/src/Module/README.md @@ -16,10 +16,16 @@ A frontend module should extend the [`BaseModule`](https://github.com/friendica/ ### Backend modules -This type of modules mainly responds either with `XML` or with `JSON`. +This type of modules mainly responds either with encoded `XML` or with `JSON` output. +It isn't intended to respond with human readable text. -Rules for Modules: -- Named like the call (i.e. https://friendica.test/contact => `Contact`) -- Start with capitals and are **not** camelCased. -- Directly interacting with a given request (POST or GET) -- Extending [`BaseModule`](https://github.com/friendica/friendica/blob/develop/src/BaseModule.php). \ No newline at end of file +A frontend module should extend the [`BaseModule`](https://github.com/friendica/friendica/blob/develop/src/BaseModule.php), especially the `rawContent()` method. + +### Routing + +Every module needs to be accessed within a route. +The routing mechanism is using a PSR-7 based routing and the routes are defined inside [`Router->collectRoutes()`](https://github.com/friendica/friendica/blob/develop/src/App/Router.php). + +Use the given routes as a pattern for further routes. + +The routing library can be found [here](https://github.com/thephpleague/route). \ No newline at end of file From b6b9e57488f77dc048887128adf3dc6d0c8892d4 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Wed, 1 May 2019 20:16:21 +0200 Subject: [PATCH 3/5] Add routes for current BaseModules --- src/App/Router.php | 8 ++++++++ src/Module/Attach.php | 4 ++-- src/Module/Contact.php | 2 ++ src/Module/Itemsource.php | 4 +--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/App/Router.php b/src/App/Router.php index 039d3e2cf..b54d5202b 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -42,6 +42,14 @@ class Router { $this->routeCollector->addRoute(['GET', 'POST'], '/itemsource[/{guid}]', Module\Itemsource::class); $this->routeCollector->addRoute(['GET'], '/amcd', Module\AccountManagementControlDocument::class); + $this->routeCollector->addRoute(['GET'], '/acctlink', Module\Acctlink::class); + $this->routeCollector->addRoute(['GET'], '/apps', Module\Apps::class); + $this->routeCollector->addRoute(['GET'], '/attach/{item:\d+}', Module\Attach::class); + $this->routeCollector->addRoute(['GET'], '/babel', Module\Babel::class); + $this->routeCollector->addGroup('/contact', function (RouteCollector $collector) { + $collector->addRoute(['GET'], '[/]', Module\Contact::class); + $collector->addRoute(['GET'], '/{id:\d+}[/posts|conversations]', Module\Contact::class); + }); } public function __construct(RouteCollector $routeCollector = null) diff --git a/src/Module/Attach.php b/src/Module/Attach.php index dd4e36805..24e0edc55 100644 --- a/src/Module/Attach.php +++ b/src/Module/Attach.php @@ -8,8 +8,8 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\L10n; -use Friendica\Core\System; use Friendica\Core\Logger; +use Friendica\Core\System; use Friendica\Model\Attach as MAttach; /** @@ -26,8 +26,8 @@ class Attach extends BaseModule if ($a->argc != 2) { System::httpExit(400); // Bad Request. } - + // @TODO: Replace with parameter from router $item_id = intval($a->argv[1]); // Check for existence diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 230ad4b57..3074bf7d6 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -46,6 +46,7 @@ class Contact extends BaseModule $contact_id = null; $contact = null; + // @TODO: Replace with parameter from router if ($a->argc == 2 && intval($a->argv[1]) || $a->argc == 3 && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations']) ) { @@ -64,6 +65,7 @@ class Contact extends BaseModule if (DBA::isResult($contact)) { if ($contact['self']) { + // @TODO: Replace with parameter from router if (($a->argc == 3) && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations'])) { $a->internalRedirect('profile/' . $contact['nick']); } else { diff --git a/src/Module/Itemsource.php b/src/Module/Itemsource.php index f92baa987..d781db3ac 100644 --- a/src/Module/Itemsource.php +++ b/src/Module/Itemsource.php @@ -2,12 +2,9 @@ namespace Friendica\Module; -use Friendica\Content\Text\HTML; use Friendica\Core\L10n; use Friendica\Core\Renderer; use Friendica\Model; -use Friendica\Protocol\ActivityPub\Processor; -use Friendica\Protocol\Diaspora; /** * @author Hypolite Petovan @@ -22,6 +19,7 @@ class Itemsource extends \Friendica\BaseModule $a = self::getApp(); + // @TODO: Replace with parameter from router if (!empty($a->argv[1])) { $guid = $a->argv[1]; } From 0b5db6238ee244dc58957f490186e805ad9b1b15 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Wed, 1 May 2019 20:17:15 +0200 Subject: [PATCH 4/5] fixing text --- src/Module/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Module/README.md b/src/Module/README.md index a2f2b9de5..a723f6a29 100644 --- a/src/Module/README.md +++ b/src/Module/README.md @@ -24,8 +24,8 @@ A frontend module should extend the [`BaseModule`](https://github.com/friendica/ ### Routing Every module needs to be accessed within a route. -The routing mechanism is using a PSR-7 based routing and the routes are defined inside [`Router->collectRoutes()`](https://github.com/friendica/friendica/blob/develop/src/App/Router.php). +The routes are defined inside [`Router->collectRoutes()`](https://github.com/friendica/friendica/blob/develop/src/App/Router.php). Use the given routes as a pattern for further routes. -The routing library can be found [here](https://github.com/thephpleague/route). \ No newline at end of file +The routing library and further documentation can be found [here](https://github.com/nikic/FastRoute). \ No newline at end of file From 07ba1b200c67ce4e4104ff1902c901c0abe8b4f8 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Wed, 1 May 2019 21:29:04 +0200 Subject: [PATCH 5/5] Add routes for current BaseModules --- src/App/Router.php | 62 +- src/Module/Feed.php | 3 + src/Module/Filer.php | 1 + src/Module/Followers.php | 4 +- src/Module/Following.php | 4 +- src/Module/Group.php | 707 ++++++++--------- src/Module/Inbox.php | 5 +- src/Module/Install.php | 1 + src/Module/Objects.php | 7 +- src/Module/Oembed.php | 1 + src/Module/Outbox.php | 4 +- src/Module/Photo.php | 2 + src/Module/Profile.php | 720 +++++++++--------- src/Module/Proxy.php | 3 +- src/Module/Register.php | 2 +- .../{Statistics_json.php => Statistics.php} | 2 +- 16 files changed, 806 insertions(+), 722 deletions(-) rename src/Module/{Statistics_json.php => Statistics.php} (97%) diff --git a/src/App/Router.php b/src/App/Router.php index b54d5202b..5b9d59f56 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -47,9 +47,67 @@ class Router $this->routeCollector->addRoute(['GET'], '/attach/{item:\d+}', Module\Attach::class); $this->routeCollector->addRoute(['GET'], '/babel', Module\Babel::class); $this->routeCollector->addGroup('/contact', function (RouteCollector $collector) { - $collector->addRoute(['GET'], '[/]', Module\Contact::class); - $collector->addRoute(['GET'], '/{id:\d+}[/posts|conversations]', Module\Contact::class); + $collector->addRoute(['GET'], '[/]', Module\Contact::class); + $collector->addRoute(['GET'], '/{id:\d+}[/posts|conversations]', Module\Contact::class); }); + $this->routeCollector->addRoute(['GET'], '/credits', Module\Credits::class); + $this->routeCollector->addGroup('/feed', function (RouteCollector $collector) { + $collector->addRoute(['GET'], '/{nickname}', Module\Feed::class); + $collector->addRoute(['GET'], '/{nickname}/posts', Module\Feed::class); + $collector->addRoute(['GET'], '/{nickname}/comments', Module\Feed::class); + $collector->addRoute(['GET'], '/{nickname}/replies', Module\Feed::class); + $collector->addRoute(['GET'], '/{nickname}/activity', Module\Feed::class); + }); + $this->routeCollector->addRoute(['GET'], '/feedtest', Module\Feedtest::class); + $this->routeCollector->addRoute(['GET'], '/filer[/{id:\d+}]', Module\Filer::class); + $this->routeCollector->addRoute(['GET'], '/followers/{owner}', Module\Followers::class); + $this->routeCollector->addRoute(['GET'], '/following/{owner}', Module\Following::class); + $this->routeCollector->addGroup('/group', function (RouteCollector $collector) { + $collector->addRoute(['GET', 'POST'], '[/]', Module\Group::class); + $collector->addRoute(['GET', 'POST'], '/{group:\d+}', Module\Group::class); + $collector->addRoute(['GET', 'POST'], '/none', Module\Group::class); + $collector->addRoute(['GET', 'POST'], '/new', Module\Group::class); + $collector->addRoute(['GET', 'POST'], '/drop/{group:\d+}', Module\Group::class); + $collector->addRoute(['GET', 'POST'], '/{group:\d+}/{contact:\d+}', Module\Group::class); + + $collector->addRoute(['POST'], '/{group:\d+}/add/{contact:\d+}', Module\Group::class); + $collector->addRoute(['POST'], '/{group:\d+}/remove/{contact:\d+}', Module\Group::class); + }); + $this->routeCollector->addRoute(['GET'], '/hashtag', Module\Hashtag::class); + $this->routeCollector->addRoute(['GET'], '/inbox[/{nickname}]', Module\Inbox::class); + $this->routeCollector->addGroup('/install', function (RouteCollector $collector) { + $collector->addRoute(['GET', 'POST'], '[/]', Module\Install::class); + $collector->addRoute(['GET'], '/testrewrite', Module\Install::class); + }); + $this->routeCollector->addRoute(['GET', 'POST'], '/localtime', Module\Localtime::class); + $this->routeCollector->addRoute(['GET', 'POST'], '/login', Module\Login::class); + $this->routeCollector->addRoute(['GET'], '/magic', Module\Magic::class); + $this->routeCollector->addRoute(['GET'], '/manifest', Module\Manifest::class); + $this->routeCollector->addRoute(['GET'], '/objects/{guid}', Module\Objects::class); + $this->routeCollector->addGroup('/oembed', function (RouteCollector $collector) { + $collector->addRoute(['GET'], '/[b2h|h2b]', Module\Oembed::class); + $collector->addRoute(['GET'], '/{hash}', Module\Oembed::class); + }); + $this->routeCollector->addRoute(['GET'], '/outbox/{owner}', Module\Outbox::class); + $this->routeCollector->addRoute(['GET'], '/owa', Module\Owa::class); + $this->routeCollector->addGroup('/photo', function (RouteCollector $collector) { + $collector->addRoute(['GET'], '/{name}', Module\Photo::class); + $collector->addRoute(['GET'], '/{type}/{name}', Module\Photo::class); + $collector->addRoute(['GET'], '/{type}/{customize}/{name}', Module\Photo::class); + }); + $this->routeCollector->addGroup('/profile', function (RouteCollector $collector) { + $collector->addRoute(['GET'], '/{nickname}', Module\Profile::class); + $collector->addRoute(['GET'], '/{profile:\d+}/view', Module\Profile::class); + }); + $this->routeCollector->addGroup('/proxy', function (RouteCollector $collector) { + $collector->addRoute(['GET'], '[/]', Module\Proxy::class); + $collector->addRoute(['GET'], '/{url}', Module\Proxy::class); + $collector->addRoute(['GET'], '/sub1/{url}', Module\Proxy::class); + $collector->addRoute(['GET'], '/sub1/sub2/{url}', Module\Proxy::class); + }); + $this->routeCollector->addRoute(['GET', 'POST'], '/register', Module\Register::class); + $this->routeCollector->addRoute(['GET'], '/statistics.json', Module\Statistics::class); + $this->routeCollector->addRoute(['GET'], '/tos', Module\Tos::class); } public function __construct(RouteCollector $routeCollector = null) diff --git a/src/Module/Feed.php b/src/Module/Feed.php index e5ebe2a4d..eabd45da2 100644 --- a/src/Module/Feed.php +++ b/src/Module/Feed.php @@ -31,11 +31,13 @@ class Feed extends BaseModule $last_update = defaults($_GET, 'last_update', ''); $nocache = !empty($_GET['nocache']) && local_user(); + // @TODO: Replace with parameter from router if ($a->argc < 2) { System::httpExit(400); } $type = null; + // @TODO: Replace with parameter from router if ($a->argc > 2) { $type = $a->argv[2]; } @@ -53,6 +55,7 @@ class Feed extends BaseModule $type = 'posts'; } + // @TODO: Replace with parameter from router $nickname = $a->argv[1]; header("Content-type: application/atom+xml; charset=utf-8"); echo OStatus::feed($nickname, $last_update, 10, $type, $nocache, true); diff --git a/src/Module/Filer.php b/src/Module/Filer.php index 08c656ed3..da59084da 100644 --- a/src/Module/Filer.php +++ b/src/Module/Filer.php @@ -28,6 +28,7 @@ class Filer extends BaseModule $logger = $a->getLogger(); $term = XML::unescape(trim(defaults($_GET, 'term', ''))); + // @TODO: Replace with parameter from router $item_id = (($a->argc > 1) ? intval($a->argv[1]) : 0); $logger->info('filer', ['tag' => $term, 'item' => $item_id]); diff --git a/src/Module/Followers.php b/src/Module/Followers.php index 56160aecf..9906dfc33 100644 --- a/src/Module/Followers.php +++ b/src/Module/Followers.php @@ -5,9 +5,9 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Protocol\ActivityPub; use Friendica\Core\System; use Friendica\Model\User; +use Friendica\Protocol\ActivityPub; /** * ActivityPub Followers @@ -18,10 +18,12 @@ class Followers extends BaseModule { $a = self::getApp(); + // @TODO: Replace with parameter from router if (empty($a->argv[1])) { System::httpExit(404); } + // @TODO: Replace with parameter from router $owner = User::getOwnerDataByNick($a->argv[1]); if (empty($owner)) { System::httpExit(404); diff --git a/src/Module/Following.php b/src/Module/Following.php index 71e6613f0..670142c48 100644 --- a/src/Module/Following.php +++ b/src/Module/Following.php @@ -5,9 +5,9 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Protocol\ActivityPub; use Friendica\Core\System; use Friendica\Model\User; +use Friendica\Protocol\ActivityPub; /** * ActivityPub Following @@ -18,10 +18,12 @@ class Following extends BaseModule { $a = self::getApp(); + // @TODO: Replace with parameter from router if (empty($a->argv[1])) { System::httpExit(404); } + // @TODO: Replace with parameter from router $owner = User::getOwnerDataByNick($a->argv[1]); if (empty($owner)) { System::httpExit(404); diff --git a/src/Module/Group.php b/src/Module/Group.php index 747ec1e56..eb1389d79 100644 --- a/src/Module/Group.php +++ b/src/Module/Group.php @@ -1,350 +1,359 @@ -isAjax()) { - self::ajaxPost(); - } - - if (!local_user()) { - notice(L10n::t('Permission denied.')); - $a->internalRedirect(); - } - - if (($a->argc == 2) && ($a->argv[1] === 'new')) { - BaseModule::checkFormSecurityTokenRedirectOnError('/group/new', 'group_edit'); - - $name = Strings::escapeTags(trim($_POST['groupname'])); - $r = Model\Group::create(local_user(), $name); - if ($r) { - info(L10n::t('Group created.')); - $r = Model\Group::getIdByName(local_user(), $name); - if ($r) { - $a->internalRedirect('group/' . $r); - } - } else { - notice(L10n::t('Could not create group.')); - } - $a->internalRedirect('group'); - } - - if (($a->argc == 2) && intval($a->argv[1])) { - BaseModule::checkFormSecurityTokenRedirectOnError('/group', 'group_edit'); - - $group = DBA::selectFirst('group', ['id', 'name'], ['id' => $a->argv[1], 'uid' => local_user()]); - if (!DBA::isResult($group)) { - notice(L10n::t('Group not found.')); - $a->internalRedirect('contact'); - } - $groupname = Strings::escapeTags(trim($_POST['groupname'])); - if (strlen($groupname) && ($groupname != $group['name'])) { - if (Model\Group::update($group['id'], $groupname)) { - info(L10n::t('Group name changed.')); - } - } - } - } - - public static function ajaxPost() - { - try { - $a = self::getApp(); - - if (!local_user()) { - throw new \Exception(L10n::t('Permission denied.'), 403); - } - - // POST /group/123/add/123 - // POST /group/123/remove/123 - if ($a->argc == 4) { - list($group_id, $command, $contact_id) = array_slice($a->argv, 1); - - if (!Model\Group::exists($group_id, local_user())) { - throw new \Exception(L10n::t('Unknown group.'), 404); - } - - $contact = DBA::selectFirst('contact', ['pending', 'blocked', 'deleted'], ['id' => $contact_id, 'uid' => local_user()]); - if (!DBA::isResult($contact)) { - throw new \Exception(L10n::t('Contact not found.'), 404); - } - - if ($contact['pending']) { - throw new \Exception(L10n::t('Contact is unavailable.'), 400); - } - - if ($contact['deleted']) { - throw new \Exception(L10n::t('Contact is deleted.'), 410); - } - - switch($command) { - case 'add': - if ($contact['blocked']) { - throw new \Exception(L10n::t('Contact is blocked, unable to add it to a group.'), 400); - } - - if (!Model\Group::addMember($group_id, $contact_id)) { - throw new \Exception(L10n::t('Unable to add the contact to the group.'), 500); - } - $message = L10n::t('Contact successfully added to group.'); - break; - case 'remove': - if (!Model\Group::removeMember($group_id, $contact_id)) { - throw new \Exception(L10n::t('Unable to remove the contact from the group.'), 500); - } - $message = L10n::t('Contact successfully removed from group.'); - break; - default: - throw new \Exception(L10n::t('Unknown group command.'), 400); - } - } else { - throw new \Exception(L10n::t('Bad request.'), 400); - } - - notice($message); - System::jsonExit(['status' => 'OK', 'message' => $message]); - } catch (\Exception $e) { - notice($e->getMessage()); - System::jsonError($e->getCode(), ['status' => 'error', 'message' => $e->getMessage()]); - } - } - - public static function content() - { - $change = false; - - if (!local_user()) { - System::httpExit(403); - } - - $a = self::getApp(); - - $a->page['aside'] = Model\Group::sidebarWidget('contact', 'group', 'extended', (($a->argc > 1) ? $a->argv[1] : 'everyone')); - - // With no group number provided we jump to the unassigned contacts as a starting point - if ($a->argc == 1) { - $a->internalRedirect('group/none'); - } - - // Switch to text mode interface if we have more than 'n' contacts or group members - $switchtotext = PConfig::get(local_user(), 'system', 'groupedit_image_limit'); - if (is_null($switchtotext)) { - $switchtotext = Config::get('system', 'groupedit_image_limit', 200); - } - - $tpl = Renderer::getMarkupTemplate('group_edit.tpl'); - - - $context = [ - '$submit' => L10n::t('Save Group'), - '$submit_filter' => L10n::t('Filter'), - ]; - - if (($a->argc == 2) && ($a->argv[1] === 'new')) { - return Renderer::replaceMacros($tpl, $context + [ - '$title' => L10n::t('Create a group of contacts/friends.'), - '$gname' => ['groupname', L10n::t('Group Name: '), '', ''], - '$gid' => 'new', - '$form_security_token' => BaseModule::getFormSecurityToken("group_edit"), - ]); - } - - $nogroup = false; - - if (($a->argc == 2) && ($a->argv[1] === 'none')) { - $id = -1; - $nogroup = true; - $group = [ - 'id' => $id, - 'name' => L10n::t('Contacts not in any group'), - ]; - - $members = []; - $preselected = []; - - $context = $context + [ - '$title' => $group['name'], - '$gname' => ['groupname', L10n::t('Group Name: '), $group['name'], ''], - '$gid' => $id, - '$editable' => 0, - ]; - } - - if (($a->argc == 3) && ($a->argv[1] === 'drop')) { - BaseModule::checkFormSecurityTokenRedirectOnError('/group', 'group_drop', 't'); - - if (intval($a->argv[2])) { - if (!Model\Group::exists($a->argv[2], local_user())) { - notice(L10n::t('Group not found.')); - $a->internalRedirect('contact'); - } - - if (Model\Group::remove($a->argv[2])) { - info(L10n::t('Group removed.')); - } else { - notice(L10n::t('Unable to remove group.')); - } - } - $a->internalRedirect('group'); - } - - if (($a->argc > 2) && intval($a->argv[1]) && intval($a->argv[2])) { - BaseModule::checkFormSecurityTokenForbiddenOnError('group_member_change', 't'); - - if (DBA::exists('contact', ['id' => $a->argv[2], 'uid' => local_user(), 'self' => false, 'pending' => false, 'blocked' => false])) { - $change = intval($a->argv[2]); - } - } - - if (($a->argc > 1) && intval($a->argv[1])) { - $group = DBA::selectFirst('group', ['id', 'name'], ['id' => $a->argv[1], 'uid' => local_user(), 'deleted' => false]); - if (!DBA::isResult($group)) { - notice(L10n::t('Group not found.')); - $a->internalRedirect('contact'); - } - - $members = Model\Contact::getByGroupId($group['id']); - $preselected = []; - - if (count($members)) { - foreach ($members as $member) { - $preselected[] = $member['id']; - } - } - - if ($change) { - if (in_array($change, $preselected)) { - Model\Group::removeMember($group['id'], $change); - } else { - Model\Group::addMember($group['id'], $change); - } - - $members = Model\Contact::getByGroupId($group['id']); - $preselected = []; - if (count($members)) { - foreach ($members as $member) { - $preselected[] = $member['id']; - } - } - } - - $drop_tpl = Renderer::getMarkupTemplate('group_drop.tpl'); - $drop_txt = Renderer::replaceMacros($drop_tpl, [ - '$id' => $group['id'], - '$delete' => L10n::t('Delete Group'), - '$form_security_token' => BaseModule::getFormSecurityToken("group_drop"), - ]); - - $context = $context + [ - '$title' => $group['name'], - '$gname' => ['groupname', L10n::t('Group Name: '), $group['name'], ''], - '$gid' => $group['id'], - '$drop' => $drop_txt, - '$form_security_token' => BaseModule::getFormSecurityToken('group_edit'), - '$edit_name' => L10n::t('Edit Group Name'), - '$editable' => 1, - ]; - } - - if (!isset($group)) { - System::httpExit(400); - } - - $groupeditor = [ - 'label_members' => L10n::t('Members'), - 'members' => [], - 'label_contacts' => L10n::t('All Contacts'), - 'group_is_empty' => L10n::t('Group is empty'), - 'contacts' => [], - ]; - - $sec_token = addslashes(BaseModule::getFormSecurityToken('group_member_change')); - - // Format the data of the group members - foreach ($members as $member) { - if ($member['url']) { - $entry = Contact::getContactTemplateVars($member); - $entry['label'] = 'members'; - $entry['photo_menu'] = ''; - $entry['change_member'] = [ - 'title' => L10n::t("Remove contact from group"), - 'gid' => $group['id'], - 'cid' => $member['id'], - 'sec_token' => $sec_token - ]; - - $groupeditor['members'][] = $entry; - } else { - Model\Group::removeMember($group['id'], $member['id']); - } - } - - if ($nogroup) { - $contacts = Model\Contact::getUngroupedList(local_user()); - } else { - $contacts_stmt = DBA::select('contact', [], - ['uid' => local_user(), 'pending' => false, 'blocked' => false, 'self' => false], - ['order' => ['name']] - ); - $contacts = DBA::toArray($contacts_stmt); - $context['$desc'] = L10n::t('Click on a contact to add or remove.'); - } - - if (DBA::isResult($contacts)) { - // Format the data of the contacts who aren't in the contact group - foreach ($contacts as $member) { - if (!in_array($member['id'], $preselected)) { - $entry = Contact::getContactTemplateVars($member); - $entry['label'] = 'contacts'; - if (!$nogroup) - $entry['photo_menu'] = []; - - if (!$nogroup) { - $entry['change_member'] = [ - 'title' => L10n::t("Add contact to group"), - 'gid' => $group['id'], - 'cid' => $member['id'], - 'sec_token' => $sec_token - ]; - } - - $groupeditor['contacts'][] = $entry; - } - } - } - - $context['$groupeditor'] = $groupeditor; - - // If there are to many contacts we could provide an alternative view mode - $total = count($groupeditor['members']) + count($groupeditor['contacts']); - $context['$shortmode'] = (($switchtotext && ($total > $switchtotext)) ? true : false); - - if ($change) { - $tpl = Renderer::getMarkupTemplate('groupeditor.tpl'); - echo Renderer::replaceMacros($tpl, $context); - exit(); - } - - return Renderer::replaceMacros($tpl, $context); - } +isAjax()) { + self::ajaxPost(); + } + + if (!local_user()) { + notice(L10n::t('Permission denied.')); + $a->internalRedirect(); + } + + // @TODO: Replace with parameter from router + if (($a->argc == 2) && ($a->argv[1] === 'new')) { + BaseModule::checkFormSecurityTokenRedirectOnError('/group/new', 'group_edit'); + + $name = Strings::escapeTags(trim($_POST['groupname'])); + $r = Model\Group::create(local_user(), $name); + if ($r) { + info(L10n::t('Group created.')); + $r = Model\Group::getIdByName(local_user(), $name); + if ($r) { + $a->internalRedirect('group/' . $r); + } + } else { + notice(L10n::t('Could not create group.')); + } + $a->internalRedirect('group'); + } + + // @TODO: Replace with parameter from router + if (($a->argc == 2) && intval($a->argv[1])) { + BaseModule::checkFormSecurityTokenRedirectOnError('/group', 'group_edit'); + + $group = DBA::selectFirst('group', ['id', 'name'], ['id' => $a->argv[1], 'uid' => local_user()]); + if (!DBA::isResult($group)) { + notice(L10n::t('Group not found.')); + $a->internalRedirect('contact'); + } + $groupname = Strings::escapeTags(trim($_POST['groupname'])); + if (strlen($groupname) && ($groupname != $group['name'])) { + if (Model\Group::update($group['id'], $groupname)) { + info(L10n::t('Group name changed.')); + } + } + } + } + + public static function ajaxPost() + { + try { + $a = self::getApp(); + + if (!local_user()) { + throw new \Exception(L10n::t('Permission denied.'), 403); + } + + // POST /group/123/add/123 + // POST /group/123/remove/123 + // @TODO: Replace with parameter from router + if ($a->argc == 4) { + list($group_id, $command, $contact_id) = array_slice($a->argv, 1); + + if (!Model\Group::exists($group_id, local_user())) { + throw new \Exception(L10n::t('Unknown group.'), 404); + } + + $contact = DBA::selectFirst('contact', ['pending', 'blocked', 'deleted'], ['id' => $contact_id, 'uid' => local_user()]); + if (!DBA::isResult($contact)) { + throw new \Exception(L10n::t('Contact not found.'), 404); + } + + if ($contact['pending']) { + throw new \Exception(L10n::t('Contact is unavailable.'), 400); + } + + if ($contact['deleted']) { + throw new \Exception(L10n::t('Contact is deleted.'), 410); + } + + switch($command) { + case 'add': + if ($contact['blocked']) { + throw new \Exception(L10n::t('Contact is blocked, unable to add it to a group.'), 400); + } + + if (!Model\Group::addMember($group_id, $contact_id)) { + throw new \Exception(L10n::t('Unable to add the contact to the group.'), 500); + } + $message = L10n::t('Contact successfully added to group.'); + break; + case 'remove': + if (!Model\Group::removeMember($group_id, $contact_id)) { + throw new \Exception(L10n::t('Unable to remove the contact from the group.'), 500); + } + $message = L10n::t('Contact successfully removed from group.'); + break; + default: + throw new \Exception(L10n::t('Unknown group command.'), 400); + } + } else { + throw new \Exception(L10n::t('Bad request.'), 400); + } + + notice($message); + System::jsonExit(['status' => 'OK', 'message' => $message]); + } catch (\Exception $e) { + notice($e->getMessage()); + System::jsonError($e->getCode(), ['status' => 'error', 'message' => $e->getMessage()]); + } + } + + public static function content() + { + $change = false; + + if (!local_user()) { + System::httpExit(403); + } + + $a = self::getApp(); + + $a->page['aside'] = Model\Group::sidebarWidget('contact', 'group', 'extended', (($a->argc > 1) ? $a->argv[1] : 'everyone')); + + // With no group number provided we jump to the unassigned contacts as a starting point + // @TODO: Replace with parameter from router + if ($a->argc == 1) { + $a->internalRedirect('group/none'); + } + + // Switch to text mode interface if we have more than 'n' contacts or group members + $switchtotext = PConfig::get(local_user(), 'system', 'groupedit_image_limit'); + if (is_null($switchtotext)) { + $switchtotext = Config::get('system', 'groupedit_image_limit', 200); + } + + $tpl = Renderer::getMarkupTemplate('group_edit.tpl'); + + + $context = [ + '$submit' => L10n::t('Save Group'), + '$submit_filter' => L10n::t('Filter'), + ]; + + // @TODO: Replace with parameter from router + if (($a->argc == 2) && ($a->argv[1] === 'new')) { + return Renderer::replaceMacros($tpl, $context + [ + '$title' => L10n::t('Create a group of contacts/friends.'), + '$gname' => ['groupname', L10n::t('Group Name: '), '', ''], + '$gid' => 'new', + '$form_security_token' => BaseModule::getFormSecurityToken("group_edit"), + ]); + } + + $nogroup = false; + + if (($a->argc == 2) && ($a->argv[1] === 'none')) { + $id = -1; + $nogroup = true; + $group = [ + 'id' => $id, + 'name' => L10n::t('Contacts not in any group'), + ]; + + $members = []; + $preselected = []; + + $context = $context + [ + '$title' => $group['name'], + '$gname' => ['groupname', L10n::t('Group Name: '), $group['name'], ''], + '$gid' => $id, + '$editable' => 0, + ]; + } + + // @TODO: Replace with parameter from router + if (($a->argc == 3) && ($a->argv[1] === 'drop')) { + BaseModule::checkFormSecurityTokenRedirectOnError('/group', 'group_drop', 't'); + + // @TODO: Replace with parameter from router + if (intval($a->argv[2])) { + if (!Model\Group::exists($a->argv[2], local_user())) { + notice(L10n::t('Group not found.')); + $a->internalRedirect('contact'); + } + + if (Model\Group::remove($a->argv[2])) { + info(L10n::t('Group removed.')); + } else { + notice(L10n::t('Unable to remove group.')); + } + } + $a->internalRedirect('group'); + } + + // @TODO: Replace with parameter from router + if (($a->argc > 2) && intval($a->argv[1]) && intval($a->argv[2])) { + BaseModule::checkFormSecurityTokenForbiddenOnError('group_member_change', 't'); + + if (DBA::exists('contact', ['id' => $a->argv[2], 'uid' => local_user(), 'self' => false, 'pending' => false, 'blocked' => false])) { + $change = intval($a->argv[2]); + } + } + + // @TODO: Replace with parameter from router + if (($a->argc > 1) && intval($a->argv[1])) { + $group = DBA::selectFirst('group', ['id', 'name'], ['id' => $a->argv[1], 'uid' => local_user(), 'deleted' => false]); + if (!DBA::isResult($group)) { + notice(L10n::t('Group not found.')); + $a->internalRedirect('contact'); + } + + $members = Model\Contact::getByGroupId($group['id']); + $preselected = []; + + if (count($members)) { + foreach ($members as $member) { + $preselected[] = $member['id']; + } + } + + if ($change) { + if (in_array($change, $preselected)) { + Model\Group::removeMember($group['id'], $change); + } else { + Model\Group::addMember($group['id'], $change); + } + + $members = Model\Contact::getByGroupId($group['id']); + $preselected = []; + if (count($members)) { + foreach ($members as $member) { + $preselected[] = $member['id']; + } + } + } + + $drop_tpl = Renderer::getMarkupTemplate('group_drop.tpl'); + $drop_txt = Renderer::replaceMacros($drop_tpl, [ + '$id' => $group['id'], + '$delete' => L10n::t('Delete Group'), + '$form_security_token' => BaseModule::getFormSecurityToken("group_drop"), + ]); + + $context = $context + [ + '$title' => $group['name'], + '$gname' => ['groupname', L10n::t('Group Name: '), $group['name'], ''], + '$gid' => $group['id'], + '$drop' => $drop_txt, + '$form_security_token' => BaseModule::getFormSecurityToken('group_edit'), + '$edit_name' => L10n::t('Edit Group Name'), + '$editable' => 1, + ]; + } + + if (!isset($group)) { + System::httpExit(400); + } + + $groupeditor = [ + 'label_members' => L10n::t('Members'), + 'members' => [], + 'label_contacts' => L10n::t('All Contacts'), + 'group_is_empty' => L10n::t('Group is empty'), + 'contacts' => [], + ]; + + $sec_token = addslashes(BaseModule::getFormSecurityToken('group_member_change')); + + // Format the data of the group members + foreach ($members as $member) { + if ($member['url']) { + $entry = Contact::getContactTemplateVars($member); + $entry['label'] = 'members'; + $entry['photo_menu'] = ''; + $entry['change_member'] = [ + 'title' => L10n::t("Remove contact from group"), + 'gid' => $group['id'], + 'cid' => $member['id'], + 'sec_token' => $sec_token + ]; + + $groupeditor['members'][] = $entry; + } else { + Model\Group::removeMember($group['id'], $member['id']); + } + } + + if ($nogroup) { + $contacts = Model\Contact::getUngroupedList(local_user()); + } else { + $contacts_stmt = DBA::select('contact', [], + ['uid' => local_user(), 'pending' => false, 'blocked' => false, 'self' => false], + ['order' => ['name']] + ); + $contacts = DBA::toArray($contacts_stmt); + $context['$desc'] = L10n::t('Click on a contact to add or remove.'); + } + + if (DBA::isResult($contacts)) { + // Format the data of the contacts who aren't in the contact group + foreach ($contacts as $member) { + if (!in_array($member['id'], $preselected)) { + $entry = Contact::getContactTemplateVars($member); + $entry['label'] = 'contacts'; + if (!$nogroup) + $entry['photo_menu'] = []; + + if (!$nogroup) { + $entry['change_member'] = [ + 'title' => L10n::t("Add contact to group"), + 'gid' => $group['id'], + 'cid' => $member['id'], + 'sec_token' => $sec_token + ]; + } + + $groupeditor['contacts'][] = $entry; + } + } + } + + $context['$groupeditor'] = $groupeditor; + + // If there are to many contacts we could provide an alternative view mode + $total = count($groupeditor['members']) + count($groupeditor['contacts']); + $context['$shortmode'] = (($switchtotext && ($total > $switchtotext)) ? true : false); + + if ($change) { + $tpl = Renderer::getMarkupTemplate('groupeditor.tpl'); + echo Renderer::replaceMacros($tpl, $context); + exit(); + } + + return Renderer::replaceMacros($tpl, $context); + } } \ No newline at end of file diff --git a/src/Module/Inbox.php b/src/Module/Inbox.php index 1bc127b7c..35160bd88 100644 --- a/src/Module/Inbox.php +++ b/src/Module/Inbox.php @@ -6,12 +6,12 @@ namespace Friendica\Module; use Friendica\BaseModule; +use Friendica\Core\Config; use Friendica\Core\Logger; -use Friendica\Protocol\ActivityPub; use Friendica\Core\System; use Friendica\Database\DBA; +use Friendica\Protocol\ActivityPub; use Friendica\Util\HTTPSignature; -use Friendica\Core\Config; /** * ActivityPub Inbox @@ -39,6 +39,7 @@ class Inbox extends BaseModule Logger::log('Incoming message stored under ' . $tempfile); } + // @TODO: Replace with parameter from router if (!empty($a->argv[1])) { $user = DBA::selectFirst('user', ['uid'], ['nickname' => $a->argv[1]]); if (!DBA::isResult($user)) { diff --git a/src/Module/Install.php b/src/Module/Install.php index 65a30e894..1cef8fbaf 100644 --- a/src/Module/Install.php +++ b/src/Module/Install.php @@ -56,6 +56,7 @@ class Install extends BaseModule // route: install/testrwrite // $baseurl/install/testrwrite to test if rewrite in .htaccess is working + // @TODO: Replace with parameter from router if ($a->getArgumentValue(1, '') == 'testrewrite') { // Status Code 204 means that it worked without content Core\System::httpExit(204); diff --git a/src/Module/Objects.php b/src/Module/Objects.php index 558b27491..f34af3a08 100644 --- a/src/Module/Objects.php +++ b/src/Module/Objects.php @@ -5,11 +5,10 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Protocol\ActivityPub; use Friendica\Core\System; -use Friendica\Model\Item; use Friendica\Database\DBA; -use Friendica\Util\HTTPSignature; +use Friendica\Model\Item; +use Friendica\Protocol\ActivityPub; /** * ActivityPub Objects @@ -32,9 +31,11 @@ class Objects extends BaseModule // $requester = HTTPSignature::getSigner('', $_SERVER); // At first we try the original post with that guid + // @TODO: Replace with parameter from router $item = Item::selectFirst(['id'], ['guid' => $a->argv[1], 'origin' => true, 'private' => false]); if (!DBA::isResult($item)) { // If no original post could be found, it could possibly be a forum post, there we remove the "origin" field. + // @TODO: Replace with parameter from router $item = Item::selectFirst(['id', 'author-link'], ['guid' => $a->argv[1], 'private' => false]); if (!DBA::isResult($item) || !strstr($item['author-link'], System::baseUrl())) { System::httpExit(404); diff --git a/src/Module/Oembed.php b/src/Module/Oembed.php index d5ff62dac..010778212 100644 --- a/src/Module/Oembed.php +++ b/src/Module/Oembed.php @@ -35,6 +35,7 @@ class Oembed extends BaseModule exit(); } + // @TODO: Replace with parameter from router if ($a->argc == 2) { echo ''; $url = Strings::base64UrlDecode($a->argv[1]); diff --git a/src/Module/Outbox.php b/src/Module/Outbox.php index 41e10757f..27dcd5c1f 100644 --- a/src/Module/Outbox.php +++ b/src/Module/Outbox.php @@ -5,10 +5,9 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Protocol\ActivityPub; use Friendica\Core\System; use Friendica\Model\User; -use Friendica\Util\HTTPSignature; +use Friendica\Protocol\ActivityPub; /** * ActivityPub Outbox @@ -19,6 +18,7 @@ class Outbox extends BaseModule { $a = self::getApp(); + // @TODO: Replace with parameter from router if (empty($a->argv[1])) { System::httpExit(404); } diff --git a/src/Module/Photo.php b/src/Module/Photo.php index ed5609640..9165af8be 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -25,6 +25,7 @@ class Photo extends BaseModule public static function init() { $a = self::getApp(); + // @TODO: Replace with parameter from router if ($a->argc <= 1 || $a->argc > 4) { System::httpExit(400); } @@ -47,6 +48,7 @@ class Photo extends BaseModule $customsize = 0; $photo = false; + // @TODO: Replace with parameter from router switch($a->argc) { case 4: $customsize = intval($a->argv[2]); diff --git a/src/Module/Profile.php b/src/Module/Profile.php index 55150a964..c3297d261 100644 --- a/src/Module/Profile.php +++ b/src/Module/Profile.php @@ -1,359 +1,361 @@ -argc < 2) { - System::httpExit(400); - } - - self::$which = filter_var($a->argv[1], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK); - - if (local_user() && $a->argc > 2 && $a->argv[2] === 'view') { - self::$which = $a->user['nickname']; - self::$profile = filter_var($a->argv[1], FILTER_SANITIZE_NUMBER_INT); - } else { - DFRN::autoRedir($a, self::$which); - } - } - - public static function rawContent() - { - if (ActivityPub::isRequest()) { - $user = DBA::selectFirst('user', ['uid'], ['nickname' => self::$which]); - $data = []; - if (DBA::isResult($user)) { - $data = ActivityPub\Transmitter::getProfile($user['uid']); - } - - if (!empty($data)) { - System::jsonExit($data, 'application/activity+json'); - } elseif (DBA::exists('userd', ['username' => self::$which])) { - // Known deleted user - $data = ActivityPub\Transmitter::getDeletedUser(self::$which); - - System::jsonError(410, $data); - } else { - // Any other case (unknown, blocked, unverified, expired, no profile, no self contact) - System::jsonError(404, $data); - } - } - } - - public static function content($update = 0) - { - $a = self::getApp(); - - if (!$update) { - ProfileModel::load($a, self::$which, self::$profile); - - $blocked = !local_user() && !remote_user() && Config::get('system', 'block_public'); - $userblock = !local_user() && !remote_user() && $a->profile['hidewall']; - - if (!empty($a->profile['page-flags']) && $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { - $a->page['htmlhead'] .= ''; - } - - if (!empty($a->profile['openidserver'])) { - $a->page['htmlhead'] .= '' . "\n"; - } - - if (!empty($a->profile['openid'])) { - $delegate = strstr($a->profile['openid'], '://') ? $a->profile['openid'] : 'https://' . $a->profile['openid']; - $a->page['htmlhead'] .= '' . "\n"; - } - - // site block - if (!$blocked && !$userblock) { - $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], defaults($a->profile, 'pub_keywords', '')); - if (strlen($keywords)) { - $a->page['htmlhead'] .= '' . "\n"; - } - } - - $a->page['htmlhead'] .= '' . "\n"; - $a->page['htmlhead'] .= '' . "\n"; - $a->page['htmlhead'] .= '' . "\n"; - $a->page['htmlhead'] .= '' . "\n"; - $a->page['htmlhead'] .= '' . "\n"; - $uri = urlencode('acct:' . $a->profile['nickname'] . '@' . $a->getHostName() . ($a->getURLPath() ? '/' . $a->getURLPath() : '')); - $a->page['htmlhead'] .= '' . "\n"; - header('Link: <' . System::baseUrl() . '/xrd/?uri=' . $uri . '>; rel="lrdd"; type="application/xrd+xml"', false); - - $dfrn_pages = ['request', 'confirm', 'notify', 'poll']; - foreach ($dfrn_pages as $dfrn) { - $a->page['htmlhead'] .= '' . "\n"; - } - $a->page['htmlhead'] .= '' . "\n"; - } - - $category = $datequery = $datequery2 = ''; - - if ($a->argc > 2) { - for ($x = 2; $x < $a->argc; $x ++) { - if (is_a_date_arg($a->argv[$x])) { - if ($datequery) { - $datequery2 = Strings::escapeHtml($a->argv[$x]); - } else { - $datequery = Strings::escapeHtml($a->argv[$x]); - } - } else { - $category = $a->argv[$x]; - } - } - } - - if (empty($category)) { - $category = defaults($_GET, 'category', ''); - } - - $hashtags = defaults($_GET, 'tag', ''); - - if (Config::get('system', 'block_public') && !local_user() && !remote_user()) { - return Login::form(); - } - - $groups = []; - $remote_cid = null; - - $o = ''; - - if ($update) { - // Ensure we've got a profile owner if updating. - $a->profile['profile_uid'] = $update; - } elseif ($a->profile['profile_uid'] == local_user()) { - Nav::setSelected('home'); - } - - $remote_contact = ContactModel::isFollower(remote_user(), $a->profile['profile_uid']); - $is_owner = local_user() == $a->profile['profile_uid']; - $last_updated_key = "profile:" . $a->profile['profile_uid'] . ":" . local_user() . ":" . remote_user(); - - if ($remote_contact) { - $cdata = ContactModel::getPublicAndUserContacID(remote_user(), $a->profile['profile_uid']); - if (!empty($cdata['user'])) { - $groups = Group::getIdsByContactId($cdata['user']); - $remote_cid = $cdata['user']; - } - } - - if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { - notice(L10n::t('Access to this profile has been restricted.') . EOL); - return ''; - } - - if (!$update) { - $tab = false; - if (!empty($_GET['tab'])) { - $tab = Strings::escapeTags(trim($_GET['tab'])); - } - - $o .= ProfileModel::getTabs($a, $is_owner, $a->profile['nickname']); - - if ($tab === 'profile') { - $o .= ProfileModel::getAdvanced($a); - Hook::callAll('profile_advanced', $o); - return $o; - } - - $o .= Widget::commonFriendsVisitor($a->profile['profile_uid']); - - $commpage = $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY; - $commvisitor = $commpage && $remote_contact; - - $a->page['aside'] .= posted_date_widget(System::baseUrl(true) . '/profile/' . $a->profile['nickname'], $a->profile['profile_uid'], true); - $a->page['aside'] .= Widget::categories(System::baseUrl(true) . '/profile/' . $a->profile['nickname'], (!empty($category) ? XML::escape($category) : '')); - $a->page['aside'] .= Widget::tagCloud(); - - if (Security::canWriteToUserWall($a->profile['profile_uid'])) { - $x = [ - 'is_owner' => $is_owner, - 'allow_location' => ($is_owner || $commvisitor) && $a->profile['allow_location'], - 'default_location' => $is_owner ? $a->user['default-location'] : '', - 'nickname' => $a->profile['nickname'], - 'lockstate' => is_array($a->user) - && (strlen($a->user['allow_cid']) - || strlen($a->user['allow_gid']) - || strlen($a->user['deny_cid']) - || strlen($a->user['deny_gid']) - ) ? 'lock' : 'unlock', - 'acl' => $is_owner ? ACL::getFullSelectorHTML($a->user, true) : '', - 'bang' => '', - 'visitor' => $is_owner || $commvisitor ? 'block' : 'none', - 'profile_uid' => $a->profile['profile_uid'], - ]; - - $o .= status_editor($a, $x); - } - } - - // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups - $sql_extra = Item::getPermissionsSQLByUserId($a->profile['profile_uid'], $remote_contact, $groups, $remote_cid); - $sql_extra2 = ''; - - if ($update) { - $last_updated = (defaults($_SESSION['last_updated'], $last_updated_key, 0)); - - // If the page user is the owner of the page we should query for unseen - // items. Otherwise use a timestamp of the last succesful update request. - if ($is_owner || !$last_updated) { - $sql_extra4 = " AND `item`.`unseen`"; - } else { - $gmupdate = gmdate(DateTimeFormat::MYSQL, $last_updated); - $sql_extra4 = " AND `item`.`received` > '" . $gmupdate . "'"; - } - - $items_stmt = DBA::p( - "SELECT DISTINCT(`parent-uri`) AS `uri`, `item`.`created` - FROM `item` - INNER JOIN `contact` - ON `contact`.`id` = `item`.`contact-id` - AND NOT `contact`.`blocked` - AND NOT `contact`.`pending` - WHERE `item`.`uid` = ? - AND `item`.`visible` - AND (NOT `item`.`deleted` OR `item`.`gravity` = ?) - AND NOT `item`.`moderated` - AND `item`.`wall` - $sql_extra4 - $sql_extra - ORDER BY `item`.`created` DESC", - $a->profile['profile_uid'], - GRAVITY_ACTIVITY - ); - - if (!DBA::isResult($items_stmt)) { - return ''; - } - - $pager = new Pager($a->query_string); - } else { - $sql_post_table = ""; - - if (!empty($category)) { - $sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", - DBA::escape(Strings::protectSprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['profile_uid'])); - } - - if (!empty($hashtags)) { - $sql_post_table .= sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", - DBA::escape(Strings::protectSprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['profile_uid'])); - } - - if (!empty($datequery)) { - $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`created` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get())))); - } - if (!empty($datequery2)) { - $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`created` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get())))); - } - - // Does the profile page belong to a forum? - // If not then we can improve the performance with an additional condition - $condition = ['uid' => $a->profile['profile_uid'], 'page-flags' => [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]]; - if (!DBA::exists('user', $condition)) { - $sql_extra3 = sprintf(" AND `thread`.`contact-id` = %d ", intval(intval($a->profile['contact_id']))); - } else { - $sql_extra3 = ""; - } - - // check if we serve a mobile device and get the user settings - // accordingly - if ($a->is_mobile) { - $itemspage_network = PConfig::get(local_user(), 'system', 'itemspage_mobile_network', 10); - } else { - $itemspage_network = PConfig::get(local_user(), 'system', 'itemspage_network', 20); - } - - // now that we have the user settings, see if the theme forces - // a maximum item number which is lower then the user choice - if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) { - $itemspage_network = $a->force_max_items; - } - - $pager = new Pager($a->query_string, $itemspage_network); - - $pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage()); - - $items_stmt = DBA::p( - "SELECT `item`.`uri` - FROM `thread` - STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - $sql_post_table - STRAIGHT_JOIN `contact` - ON `contact`.`id` = `thread`.`contact-id` - AND NOT `contact`.`blocked` - AND NOT `contact`.`pending` - WHERE `thread`.`uid` = ? - AND `thread`.`visible` - AND NOT `thread`.`deleted` - AND NOT `thread`.`moderated` - AND `thread`.`wall` - $sql_extra3 - $sql_extra - $sql_extra2 - ORDER BY `thread`.`created` DESC - $pager_sql", - $a->profile['profile_uid'] - ); - } - - // Set a time stamp for this page. We will make use of it when we - // search for new items (update routine) - $_SESSION['last_updated'][$last_updated_key] = time(); - - if ($is_owner && !$update && !Config::get('theme', 'hide_eventlist')) { - $o .= ProfileModel::getBirthdays(); - $o .= ProfileModel::getEventsReminderHTML(); - } - - if ($is_owner) { - $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); - if ($unseen) { - Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); - } - } - - $items = DBA::toArray($items_stmt); - - $o .= conversation($a, $items, $pager, 'profile', $update, false, 'created', $a->profile['profile_uid']); - - if (!$update) { - $o .= $pager->renderMinimal(count($items)); - } - - return $o; - } -} +argc < 2) { + System::httpExit(400); + } + + self::$which = filter_var($a->argv[1], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK); + + // @TODO: Replace with parameter from router + if (local_user() && $a->argc > 2 && $a->argv[2] === 'view') { + self::$which = $a->user['nickname']; + self::$profile = filter_var($a->argv[1], FILTER_SANITIZE_NUMBER_INT); + } else { + DFRN::autoRedir($a, self::$which); + } + } + + public static function rawContent() + { + if (ActivityPub::isRequest()) { + $user = DBA::selectFirst('user', ['uid'], ['nickname' => self::$which]); + $data = []; + if (DBA::isResult($user)) { + $data = ActivityPub\Transmitter::getProfile($user['uid']); + } + + if (!empty($data)) { + System::jsonExit($data, 'application/activity+json'); + } elseif (DBA::exists('userd', ['username' => self::$which])) { + // Known deleted user + $data = ActivityPub\Transmitter::getDeletedUser(self::$which); + + System::jsonError(410, $data); + } else { + // Any other case (unknown, blocked, unverified, expired, no profile, no self contact) + System::jsonError(404, $data); + } + } + } + + public static function content($update = 0) + { + $a = self::getApp(); + + if (!$update) { + ProfileModel::load($a, self::$which, self::$profile); + + $blocked = !local_user() && !remote_user() && Config::get('system', 'block_public'); + $userblock = !local_user() && !remote_user() && $a->profile['hidewall']; + + if (!empty($a->profile['page-flags']) && $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { + $a->page['htmlhead'] .= ''; + } + + if (!empty($a->profile['openidserver'])) { + $a->page['htmlhead'] .= '' . "\n"; + } + + if (!empty($a->profile['openid'])) { + $delegate = strstr($a->profile['openid'], '://') ? $a->profile['openid'] : 'https://' . $a->profile['openid']; + $a->page['htmlhead'] .= '' . "\n"; + } + + // site block + if (!$blocked && !$userblock) { + $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], defaults($a->profile, 'pub_keywords', '')); + if (strlen($keywords)) { + $a->page['htmlhead'] .= '' . "\n"; + } + } + + $a->page['htmlhead'] .= '' . "\n"; + $a->page['htmlhead'] .= '' . "\n"; + $a->page['htmlhead'] .= '' . "\n"; + $a->page['htmlhead'] .= '' . "\n"; + $a->page['htmlhead'] .= '' . "\n"; + $uri = urlencode('acct:' . $a->profile['nickname'] . '@' . $a->getHostName() . ($a->getURLPath() ? '/' . $a->getURLPath() : '')); + $a->page['htmlhead'] .= '' . "\n"; + header('Link: <' . System::baseUrl() . '/xrd/?uri=' . $uri . '>; rel="lrdd"; type="application/xrd+xml"', false); + + $dfrn_pages = ['request', 'confirm', 'notify', 'poll']; + foreach ($dfrn_pages as $dfrn) { + $a->page['htmlhead'] .= '' . "\n"; + } + $a->page['htmlhead'] .= '' . "\n"; + } + + $category = $datequery = $datequery2 = ''; + + if ($a->argc > 2) { + for ($x = 2; $x < $a->argc; $x ++) { + if (is_a_date_arg($a->argv[$x])) { + if ($datequery) { + $datequery2 = Strings::escapeHtml($a->argv[$x]); + } else { + $datequery = Strings::escapeHtml($a->argv[$x]); + } + } else { + $category = $a->argv[$x]; + } + } + } + + if (empty($category)) { + $category = defaults($_GET, 'category', ''); + } + + $hashtags = defaults($_GET, 'tag', ''); + + if (Config::get('system', 'block_public') && !local_user() && !remote_user()) { + return Login::form(); + } + + $groups = []; + $remote_cid = null; + + $o = ''; + + if ($update) { + // Ensure we've got a profile owner if updating. + $a->profile['profile_uid'] = $update; + } elseif ($a->profile['profile_uid'] == local_user()) { + Nav::setSelected('home'); + } + + $remote_contact = ContactModel::isFollower(remote_user(), $a->profile['profile_uid']); + $is_owner = local_user() == $a->profile['profile_uid']; + $last_updated_key = "profile:" . $a->profile['profile_uid'] . ":" . local_user() . ":" . remote_user(); + + if ($remote_contact) { + $cdata = ContactModel::getPublicAndUserContacID(remote_user(), $a->profile['profile_uid']); + if (!empty($cdata['user'])) { + $groups = Group::getIdsByContactId($cdata['user']); + $remote_cid = $cdata['user']; + } + } + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { + notice(L10n::t('Access to this profile has been restricted.') . EOL); + return ''; + } + + if (!$update) { + $tab = false; + if (!empty($_GET['tab'])) { + $tab = Strings::escapeTags(trim($_GET['tab'])); + } + + $o .= ProfileModel::getTabs($a, $is_owner, $a->profile['nickname']); + + if ($tab === 'profile') { + $o .= ProfileModel::getAdvanced($a); + Hook::callAll('profile_advanced', $o); + return $o; + } + + $o .= Widget::commonFriendsVisitor($a->profile['profile_uid']); + + $commpage = $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY; + $commvisitor = $commpage && $remote_contact; + + $a->page['aside'] .= posted_date_widget(System::baseUrl(true) . '/profile/' . $a->profile['nickname'], $a->profile['profile_uid'], true); + $a->page['aside'] .= Widget::categories(System::baseUrl(true) . '/profile/' . $a->profile['nickname'], (!empty($category) ? XML::escape($category) : '')); + $a->page['aside'] .= Widget::tagCloud(); + + if (Security::canWriteToUserWall($a->profile['profile_uid'])) { + $x = [ + 'is_owner' => $is_owner, + 'allow_location' => ($is_owner || $commvisitor) && $a->profile['allow_location'], + 'default_location' => $is_owner ? $a->user['default-location'] : '', + 'nickname' => $a->profile['nickname'], + 'lockstate' => is_array($a->user) + && (strlen($a->user['allow_cid']) + || strlen($a->user['allow_gid']) + || strlen($a->user['deny_cid']) + || strlen($a->user['deny_gid']) + ) ? 'lock' : 'unlock', + 'acl' => $is_owner ? ACL::getFullSelectorHTML($a->user, true) : '', + 'bang' => '', + 'visitor' => $is_owner || $commvisitor ? 'block' : 'none', + 'profile_uid' => $a->profile['profile_uid'], + ]; + + $o .= status_editor($a, $x); + } + } + + // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups + $sql_extra = Item::getPermissionsSQLByUserId($a->profile['profile_uid'], $remote_contact, $groups, $remote_cid); + $sql_extra2 = ''; + + if ($update) { + $last_updated = (defaults($_SESSION['last_updated'], $last_updated_key, 0)); + + // If the page user is the owner of the page we should query for unseen + // items. Otherwise use a timestamp of the last succesful update request. + if ($is_owner || !$last_updated) { + $sql_extra4 = " AND `item`.`unseen`"; + } else { + $gmupdate = gmdate(DateTimeFormat::MYSQL, $last_updated); + $sql_extra4 = " AND `item`.`received` > '" . $gmupdate . "'"; + } + + $items_stmt = DBA::p( + "SELECT DISTINCT(`parent-uri`) AS `uri`, `item`.`created` + FROM `item` + INNER JOIN `contact` + ON `contact`.`id` = `item`.`contact-id` + AND NOT `contact`.`blocked` + AND NOT `contact`.`pending` + WHERE `item`.`uid` = ? + AND `item`.`visible` + AND (NOT `item`.`deleted` OR `item`.`gravity` = ?) + AND NOT `item`.`moderated` + AND `item`.`wall` + $sql_extra4 + $sql_extra + ORDER BY `item`.`created` DESC", + $a->profile['profile_uid'], + GRAVITY_ACTIVITY + ); + + if (!DBA::isResult($items_stmt)) { + return ''; + } + + $pager = new Pager($a->query_string); + } else { + $sql_post_table = ""; + + if (!empty($category)) { + $sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", + DBA::escape(Strings::protectSprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['profile_uid'])); + } + + if (!empty($hashtags)) { + $sql_post_table .= sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", + DBA::escape(Strings::protectSprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['profile_uid'])); + } + + if (!empty($datequery)) { + $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`created` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get())))); + } + if (!empty($datequery2)) { + $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`created` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get())))); + } + + // Does the profile page belong to a forum? + // If not then we can improve the performance with an additional condition + $condition = ['uid' => $a->profile['profile_uid'], 'page-flags' => [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]]; + if (!DBA::exists('user', $condition)) { + $sql_extra3 = sprintf(" AND `thread`.`contact-id` = %d ", intval(intval($a->profile['contact_id']))); + } else { + $sql_extra3 = ""; + } + + // check if we serve a mobile device and get the user settings + // accordingly + if ($a->is_mobile) { + $itemspage_network = PConfig::get(local_user(), 'system', 'itemspage_mobile_network', 10); + } else { + $itemspage_network = PConfig::get(local_user(), 'system', 'itemspage_network', 20); + } + + // now that we have the user settings, see if the theme forces + // a maximum item number which is lower then the user choice + if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) { + $itemspage_network = $a->force_max_items; + } + + $pager = new Pager($a->query_string, $itemspage_network); + + $pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage()); + + $items_stmt = DBA::p( + "SELECT `item`.`uri` + FROM `thread` + STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` + $sql_post_table + STRAIGHT_JOIN `contact` + ON `contact`.`id` = `thread`.`contact-id` + AND NOT `contact`.`blocked` + AND NOT `contact`.`pending` + WHERE `thread`.`uid` = ? + AND `thread`.`visible` + AND NOT `thread`.`deleted` + AND NOT `thread`.`moderated` + AND `thread`.`wall` + $sql_extra3 + $sql_extra + $sql_extra2 + ORDER BY `thread`.`created` DESC + $pager_sql", + $a->profile['profile_uid'] + ); + } + + // Set a time stamp for this page. We will make use of it when we + // search for new items (update routine) + $_SESSION['last_updated'][$last_updated_key] = time(); + + if ($is_owner && !$update && !Config::get('theme', 'hide_eventlist')) { + $o .= ProfileModel::getBirthdays(); + $o .= ProfileModel::getEventsReminderHTML(); + } + + if ($is_owner) { + $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); + if ($unseen) { + Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); + } + } + + $items = DBA::toArray($items_stmt); + + $o .= conversation($a, $items, $pager, 'profile', $update, false, 'created', $a->profile['profile_uid']); + + if (!$update) { + $o .= $pager->renderMinimal(count($items)); + } + + return $o; + } +} diff --git a/src/Module/Proxy.php b/src/Module/Proxy.php index 54870abe0..387667a1a 100644 --- a/src/Module/Proxy.php +++ b/src/Module/Proxy.php @@ -12,7 +12,6 @@ use Friendica\Model\Photo; use Friendica\Object\Image; use Friendica\Util\HTTPSignature; use Friendica\Util\Proxy as ProxyUtils; -use Friendica\Core\Logger; /** * @brief Module Proxy @@ -159,6 +158,7 @@ class Proxy extends BaseModule $sizetype = ''; // Look for filename in the arguments + // @TODO: Replace with parameter from router if (($a->argc > 1) && !isset($_REQUEST['url'])) { if (isset($a->argv[3])) { $url = $a->argv[3]; @@ -169,6 +169,7 @@ class Proxy extends BaseModule } /// @TODO: Why? And what about $url in this case? + /// @TODO: Replace with parameter from router if (isset($a->argv[3]) && ($a->argv[3] == 'thumb')) { $size = 200; } diff --git a/src/Module/Register.php b/src/Module/Register.php index 6744a0a5f..202cf948e 100644 --- a/src/Module/Register.php +++ b/src/Module/Register.php @@ -19,7 +19,7 @@ use Friendica\Util\Strings; /** * @author Hypolite Petovan */ -abstract class Register extends BaseModule +class Register extends BaseModule { const CLOSED = 0; const APPROVE = 1; diff --git a/src/Module/Statistics_json.php b/src/Module/Statistics.php similarity index 97% rename from src/Module/Statistics_json.php rename to src/Module/Statistics.php index aca204b92..566ace333 100644 --- a/src/Module/Statistics_json.php +++ b/src/Module/Statistics.php @@ -6,7 +6,7 @@ use Friendica\BaseModule; use Friendica\Core\Addon; use Friendica\Core\System; -class Statistics_json extends BaseModule +class Statistics extends BaseModule { public static function init() {