From c571ca40d7cb6642ce7243c3f6cc09b9c7a29ad8 Mon Sep 17 00:00:00 2001 From: Klaus Weidenbach Date: Tue, 31 Oct 2017 21:23:42 +0100 Subject: :white_check_mark: unit tests for Zotlabs\Access classes. --- tests/unit/Access/PermissionLimitsTest.php | 78 +++++++++ tests/unit/Access/PermissionRolesTest.php | 100 ++++++++++++ tests/unit/Access/PermissionsTest.php | 244 ++++++++++++++++++++++++----- 3 files changed, 387 insertions(+), 35 deletions(-) create mode 100644 tests/unit/Access/PermissionLimitsTest.php create mode 100644 tests/unit/Access/PermissionRolesTest.php diff --git a/tests/unit/Access/PermissionLimitsTest.php b/tests/unit/Access/PermissionLimitsTest.php new file mode 100644 index 000000000..58595111a --- /dev/null +++ b/tests/unit/Access/PermissionLimitsTest.php @@ -0,0 +1,78 @@ +getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->exactly($permsCount)); + + $stdlimits = PermissionLimits::Std_Limits(); + $this->assertCount($permsCount, $stdlimits, "There should be $permsCount permissions."); + + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_stream']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['send_stream']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_profile']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_contacts']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_storage']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_storage']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_pages']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['view_wiki']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_pages']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['write_wiki']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_wall']); + $this->assertEquals(PERMS_PUBLIC, $stdlimits['post_comments']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_mail']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['post_like']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['tag_deliver']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['chat']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['republish']); + $this->assertEquals(PERMS_SPECIFIC, $stdlimits['delegate']); + } + +} \ No newline at end of file diff --git a/tests/unit/Access/PermissionRolesTest.php b/tests/unit/Access/PermissionRolesTest.php new file mode 100644 index 000000000..5e64e773a --- /dev/null +++ b/tests/unit/Access/PermissionRolesTest.php @@ -0,0 +1,100 @@ +assertEquals($expectedVersion, PermissionRoles::version()); + + $pr = new PermissionRoles(); + $this->assertEquals($expectedVersion, $pr->version()); + } + + + public function testRoles() { + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->atLeastOnce())->willReturnCallback( + function ($string) { + return $string; + } + ); + + $roles = PermissionRoles::roles(); + $r = new PermissionRoles(); + $this->assertEquals($roles, $r->roles()); + + $socialNetworking = [ + 'social' => 'Social - Mostly Public', + 'social_restricted' => 'Social - Restricted', + 'social_private' => 'Social - Private' + ]; + + $this->assertArraySubset(['Social Networking' => $socialNetworking], $roles); + $this->assertEquals($socialNetworking, $roles['Social Networking']); + + $this->assertCount(5, $roles, 'There should be 5 permission groups.'); + + $this->assertCount(1, $roles['Other'], "In the 'Other' group should be just one permission role"); + } + + + /** + * @uses ::call_hooks + * @uses Zotlabs\Access\PermissionLimits::Std_Limits + * @uses Zotlabs\Access\Permissions::Perms + */ + public function testRole_perms() { + // Create a stub for global function t() + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t = $this->getFunctionMock('Zotlabs\Access', 'get_config'); + + $rp_social = PermissionRoles::role_perms('social'); + $this->assertEquals('social', $rp_social['role']); + + + $rp_custom = PermissionRoles::role_perms('custom'); + $this->assertEquals(['role' => 'custom'], $rp_custom); + + $rp_nonexistent = PermissionRoles::role_perms('nonexistent'); + $this->assertEquals(['role' => 'nonexistent'], $rp_nonexistent); + } + +} \ No newline at end of file diff --git a/tests/unit/Access/PermissionsTest.php b/tests/unit/Access/PermissionsTest.php index 73d0e7827..40724fff8 100644 --- a/tests/unit/Access/PermissionsTest.php +++ b/tests/unit/Access/PermissionsTest.php @@ -23,6 +23,7 @@ namespace Zotlabs\Tests\Unit\Access; +use phpmock\phpunit\PHPMock; use Zotlabs\Tests\Unit\UnitTestCase; use Zotlabs\Access\Permissions; @@ -33,54 +34,228 @@ use Zotlabs\Access\Permissions; */ class PermissionsTest extends UnitTestCase { + use PHPMock; + + public function testVersion() { + $expectedVersion = 2; + + // static call + $this->assertEquals($expectedVersion, Permissions::version()); + + // instance call + $p = new Permissions(); + $this->assertEquals($expectedVersion, $p->version()); + } + + /** + * @coversNothing + */ + public function testVersionEqualsPermissionRoles() { + $p = new Permissions(); + $pr = new \Zotlabs\Access\PermissionRoles(); + $this->assertEquals($p->version(), $pr->version()); + } + + /** + * @uses ::call_hooks + */ + public function testPerms() { + // There are 18 default perms + $permsCount = 18; + + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->exactly(2*$permsCount))->willReturnCallback( + function ($string) { + return $string; + } + ); + + // static method Perms() + $perms = Permissions::Perms(); + + $p = new Permissions(); + $this->assertEquals($perms, $p->Perms()); + + $this->assertEquals($permsCount, count($perms), "There should be $permsCount permissions."); + + $this->assertEquals('Can view my channel stream and posts', $perms['view_stream']); + + // non existent perm should not be set + $this->assertFalse(isset($perms['invalid_perm'])); + } + + /** + * filter parmeter is only used in hook \b permissions_list. So the result + * in this test should be the same as if there was no filter parameter. + * + * @todo Stub call_hooks() function and also test filter + * + * @uses ::call_hooks + */ + public function testPermsFilter() { + // There are 18 default perms + $permsCount = 18; + + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->exactly(2*$permsCount))->willReturnCallback( + function ($string) { + return $string; + } + ); + + $perms = Permissions::Perms('view_'); + $this->assertEquals($permsCount, count($perms)); + + $this->assertEquals('Can view my channel stream and posts', $perms['view_stream']); + + $perms = Permissions::Perms('invalid_perm'); + $this->assertEquals($permsCount, count($perms)); + } + /** + * Better should mock Permissions::Perms, but not possible with static methods. + * + * @uses ::call_hooks + * * @dataProvider FilledPermsProvider + * + * @param array $permarr An indexed permissions array to pass + * @param array $expected The expected result perms array */ public function testFilledPerms($permarr, $expected) { - $this->markTestIncomplete( - 'Need to mock static function Permissions::Perms() ...' - ); - //$this->assertEquals($expected, Permissions::FilledPerms($permarr)); - -/* $perms = $this->getMockBuilder(Permissions::class) - ->setMethods(['Perms']) - ->getMock(); - $perms->expects($this->once()) - ->method('Perms'); - // still calls the static self::Perms() - $perms->FilledPerms($permarr); -*/ + // Create a stub for global function t() + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + + $this->assertEquals($expected, Permissions::FilledPerms($permarr)); } + /** + * @return array An associative array with test values for FilledPerms() + * * \e array Indexed array which is passed as parameter to FilledPerms() + * * \e array Expected associative result array with filled perms + */ public function FilledPermsProvider() { return [ - 'empty' => [ + 'Empty param array' => [ [], - ['perm1' => 0, 'perm2' => 0] + [ + 'view_stream' => 0, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 0, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ] ], - 'valid' => [ - [['perm1' => 1]], - ['perm1' => 1, 'perm2' => 0] + 'provide view_stream and view_pages as param' => [ + ['view_stream', 'view_pages'], + [ + 'view_stream' => 1, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 1, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ] + ], + 'provide an unknown param' => [ + ['view_stream', 'unknown_perm'], + [ + 'view_stream' => 1, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 0, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ] ] ]; } -/* public function testFilledPermsNull() { - // need to mock global function btlogger(); - Permissions::FilledPerms(null); + /** + * @uses ::call_hooks + */ + public function testFilledPermsNull() { + // Create a stub for global function t() with expectation + $t = $this->getFunctionMock('Zotlabs\Access', 't'); + $t->expects($this->atLeastOnce()); + // Create a stub for global function bt() with expectations + $bt = $this->getFunctionMock('Zotlabs\Access', 'btlogger'); + $bt->expects($this->once())->with($this->equalTo('FilledPerms: null')); + + $result = [ + 'view_stream' => 0, + 'send_stream' => 0, + 'view_profile' => 0, + 'view_contacts' => 0, + 'view_storage' => 0, + 'write_storage' => 0, + 'view_pages' => 0, + 'view_wiki' => 0, + 'write_pages' => 0, + 'write_wiki' => 0, + 'post_wall' => 0, + 'post_comments' => 0, + 'post_mail' => 0, + 'post_like' => 0, + 'tag_deliver' => 0, + 'chat' => 0, + 'republish' => 0, + 'delegate' => 0 + ]; + + $this->assertEquals($result, Permissions::FilledPerms(null)); } -*/ + /** * @dataProvider OPermsProvider * - * @param array $permarr - * @param array $expected + * @param array $permarr The params to pass to the OPerms method + * @param array $expected The expected result */ public function testOPerms($permarr, $expected) { $this->assertEquals($expected, Permissions::OPerms($permarr)); } /** - * @return Associative array with test values for OPerms() - * * \e array Array to test - * * \e array Expect array + * @return array An associative array with test values for OPerms() + * * \e array Array with perms to test + * * \e array Expected result array */ public function OPermsProvider() { return [ @@ -99,22 +274,21 @@ class PermissionsTest extends UnitTestCase { ]; } - /** * @dataProvider permsCompareProvider * - * @param array $p1 - * @param array $p2 - * @param boolean $expectedresult + * @param array $p1 The first permission + * @param array $p2 The second permission + * @param boolean $expectedresult The expected result of the tested method */ public function testPermsCompare($p1, $p2, $expectedresult) { $this->assertEquals($expectedresult, Permissions::PermsCompare($p1, $p2)); } /** - * @return Associative array with test values for PermsCompare() - * * \e array 1st array - * * \e array 2nd array - * * \e boolean expected result for the test + * @return array An associative array with test values for PermsCompare() + * * \e array 1st array with perms + * * \e array 2nd array with perms + * * \e boolean expected result for the perms comparison */ public function permsCompareProvider() { return [ -- cgit v1.2.3 From 6cf3ebb7dbf813d92342d0350078edbcd5050e9c Mon Sep 17 00:00:00 2001 From: Klaus Weidenbach Date: Mon, 6 Nov 2017 23:28:44 +0100 Subject: :ok_hand: Fix a PHP warning in Permissions::FilledPerms(). When NULL is passed as parameter there is a PHP warning when testing against it in the function in_array(). --- Zotlabs/Access/Permissions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Zotlabs/Access/Permissions.php b/Zotlabs/Access/Permissions.php index 20ce21238..bca40a9c1 100644 --- a/Zotlabs/Access/Permissions.php +++ b/Zotlabs/Access/Permissions.php @@ -127,6 +127,7 @@ class Permissions { static public function FilledPerms($arr) { if(is_null($arr)) { btlogger('FilledPerms: null'); + $arr = []; } $everything = self::Perms(); -- cgit v1.2.3 From 458f2e748de22448206ce12c9961e9cce80c796d Mon Sep 17 00:00:00 2001 From: zotlabs Date: Sun, 12 Nov 2017 14:14:20 -0800 Subject: cleanup of upload_to_comments test; we actually don't care about the profile owner. We only care that we're logged in locally and our storage is public by default. --- Zotlabs/Module/Item.php | 9 --------- include/conversation.php | 22 ++++++++-------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index b54de0fb9..f2b850ffc 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -577,15 +577,6 @@ class Item extends \Zotlabs\Web\Controller { * so we'll set the permissions regardless and realise that the media may not be * referenced in the post. * - * What is preventing us from being able to upload photos into comments is dealing with - * the photo and attachment permissions, since we don't always know who was in the - * distribution for the top level post. - * - * We might be able to provide this functionality with a lot of fiddling: - * - if the top level post is public (make the photo public) - * - if the top level post was written by us or a wall post that belongs to us (match the top level post) - * - if the top level post has privacy mentions, add those to the permissions. - * - otherwise disallow the photo *or* make the photo public. This is the part that gets messy. */ if(! $preview) { diff --git a/include/conversation.php b/include/conversation.php index 0b9df5acd..1cbd9116c 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -573,22 +573,16 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa if (! feature_enabled($profile_owner,'multi_delete')) $page_dropping = false; - $uploading = true; - - if($profile_owner > 0) { - $owner_channel = channelx_by_n($profile_owner); - if($owner_channel['channel_allow_cid'] || $owner_channel['channel_allow_gid'] - || $owner_channel['channel_deny_cid'] || $owner_channel['channel_deny_gid']) { - $uploading = false; - } - if(\Zotlabs\Access\PermissionLimits::Get($profile_owner,'view_storage') !== PERMS_PUBLIC) { - $uploading = false; + $uploading = false; + + if(local_channel()) { + $cur_channel = App::get_channel(); + if($cur_channel['channel_allow_cid'] === '' && $cur_channel['channel_allow_gid'] === '' + && $cur_channel['channel_deny_cid'] === '' && $cur_channel['channel_deny_gid'] === '' + && intval(\Zotlabs\Access\PermissionLimits::Get(local_channel(),'view_storage')) === PERMS_PUBLIC) { + $uploading = true; } } - else { - $uploading = false; - } - $channel = App::get_channel(); $observer = App::get_observer(); -- cgit v1.2.3 From 1ac35dd04748744b22e6fc3dd254f16f7980bd90 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Sun, 12 Nov 2017 15:07:12 -0800 Subject: fix some conflicts between private tags and forum tags --- include/items.php | 304 +++++++++++++++++++++++++++--------------------------- 1 file changed, 150 insertions(+), 154 deletions(-) diff --git a/include/items.php b/include/items.php index 5d592c736..b1b40e977 100755 --- a/include/items.php +++ b/include/items.php @@ -2571,148 +2571,149 @@ function tag_deliver($uid, $item_id) { if($terms) logger('Post mentions: ' . print_r($terms,true), LOGGER_DATA); + + $max_forums = get_config('system','max_tagged_forums',2); + $matched_forums = 0; + + $link = normalise_link($u[0]['xchan_url']); + if($terms) { foreach($terms as $term) { - if(link_compare($term['url'],$link)) { - $mention = true; - break; + if(! link_compare($term['url'],$link)) { + continue; } - } - } - if($mention) { - logger('Mention found for ' . $u[0]['channel_name']); + $mention = true; - $r = q("update item set item_mentionsme = 1 where id = %d", - intval($item_id) - ); + logger('Mention found for ' . $u[0]['channel_name']); - // At this point we've determined that the person receiving this post was mentioned in it or it is a union. - // Now let's check if this mention was inside a reshare so we don't spam a forum - // If it's private we may have to unobscure it momentarily so that we can parse it. + $r = q("update item set item_mentionsme = 1 where id = %d", + intval($item_id) + ); - $body = $item['body']; + // At this point we've determined that the person receiving this post was mentioned in it or it is a union. + // Now let's check if this mention was inside a reshare so we don't spam a forum + // If it's private we may have to unobscure it momentarily so that we can parse it. - $body = preg_replace('/\[share(.*?)\[\/share\]/','',$body); + $body = preg_replace('/\[share(.*?)\[\/share\]/','',$item['body']); - $tagged = false; - $plustagged = false; - $matches = array(); + $tagged = false; + $plustagged = false; + $matches = array(); - $pattern = '/[\!@]\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'],'/') . '\[\/zrl\]/'; - if(preg_match($pattern,$body,$matches)) - $tagged = true; + $pattern = '/[\!@]\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'],'/') . '\[\/zrl\]/'; + if(preg_match($pattern,$body,$matches)) + $tagged = true; - // original red forum tagging sequence @forumname+ - // standard forum tagging sequence !forumname + // original red forum tagging sequence @forumname+ + // standard forum tagging sequence !forumname - $pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; + $pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; - $forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/'; + $forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/'; - $found = false; + $found = false; - $max_forums = get_config('system','max_tagged_forums'); - if(! $max_forums) - $max_forums = 2; - $matched_forums = 0; - $matches = array(); + $matches = array(); - if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) { - foreach($matches as $match) { - $matched_forums ++; - if($term['url'] === $match[1] && $term['term'] === $match[2]) { - if($matched_forums <= $max_forums) { - $plustagged = true; - $found = true; - break; + if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) { + foreach($matches as $match) { + $matched_forums ++; + if($term['url'] === $match[1] && $term['term'] === $match[2] && intval($term['ttype']) === TERM_MENTION) { + if($matched_forums <= $max_forums) { + $plustagged = true; + $found = true; + break; + } + logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); } - logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); } } - } - if(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) { - foreach($matches as $match) { - $matched_forums ++; - if($term['url'] === $match[1] && $term['term'] === $match[2]) { - if($matched_forums <= $max_forums) { - $plustagged = true; - $found = true; - break; + if(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) { + foreach($matches as $match) { + $matched_forums ++; + if($term['url'] === $match[1] && $term['term'] === $match[2] && intval($term['ttype']) === TERM_FORUM) { + if($matched_forums <= $max_forums) { + $plustagged = true; + $found = true; + break; + } + logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); } - logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); } } - } - if(! ($tagged || $plustagged)) { - logger('Mention was in a reshare or exceeded max_tagged_forums - ignoring'); - return; - } + if(! ($tagged || $plustagged)) { + logger('Mention was in a reshare or exceeded max_tagged_forums - ignoring'); + continue; + } - $arr = [ - 'channel_id' => $uid, - 'item' => $item, - 'body' => $body - ]; - /** - * @hooks tagged - * Called when a delivery is processed which results in you being tagged. - * * \e number \b channel_id - * * \e array \b item - * * \e string \b body - */ - call_hooks('tagged', $arr); + $arr = [ + 'channel_id' => $uid, + 'item' => $item, + 'body' => $body + ]; + /** + * @hooks tagged + * Called when a delivery is processed which results in you being tagged. + * * \e number \b channel_id + * * \e array \b item + * * \e string \b body + */ + call_hooks('tagged', $arr); + + /* + * Kill two birds with one stone. As long as we're here, send a mention notification. + */ - /* - * Kill two birds with one stone. As long as we're here, send a mention notification. - */ + Zlib\Enotify::submit(array( + 'to_xchan' => $u[0]['channel_hash'], + 'from_xchan' => $item['author_xchan'], + 'type' => NOTIFY_TAGSELF, + 'item' => $item, + 'link' => $i[0]['llink'], + 'verb' => ACTIVITY_TAG, + 'otype' => 'item' + )); - Zlib\Enotify::submit(array( - 'to_xchan' => $u[0]['channel_hash'], - 'from_xchan' => $item['author_xchan'], - 'type' => NOTIFY_TAGSELF, - 'item' => $item, - 'link' => $i[0]['llink'], - 'verb' => ACTIVITY_TAG, - 'otype' => 'item' - )); - - // Just a normal tag? - - if(! $plustagged) { - logger('Not a plus tag', LOGGER_DEBUG); - return; - } + // Just a normal tag? - // plustagged - keep going, next check permissions + if(! $plustagged) { + logger('Not a plus tag', LOGGER_DEBUG); + continue; + } - if(! perm_is_allowed($uid,$item['author_xchan'],'tag_deliver')) { - logger('tag_delivery denied for uid ' . $uid . ' and xchan ' . $item['author_xchan']); - return; - } - } + // plustagged - keep going, next check permissions + + if(! perm_is_allowed($uid,$item['author_xchan'],'tag_deliver')) { + logger('tag_delivery denied for uid ' . $uid . ' and xchan ' . $item['author_xchan']); + continue; + } - if((! $mention) && (! $union)) { - logger('No mention for ' . $u[0]['channel_name'] . ' and no union.'); - return; - } - // tgroup delivery - setup a second delivery chain - // prevent delivery looping - only proceed - // if the message originated elsewhere and is a top-level post + if((! $mention) && (! $union)) { + logger('No mention for ' . $u[0]['channel_name'] . ' and no union.'); + continue; + } + // tgroup delivery - setup a second delivery chain + // prevent delivery looping - only proceed + // if the message originated elsewhere and is a top-level post - if(intval($item['item_wall']) || intval($item['item_origin']) || (! intval($item['item_thread_top'])) || ($item['id'] != $item['parent'])) { - logger('Item was local or a comment. rejected.'); - return; - } - logger('Creating second delivery chain.'); - start_delivery_chain($u[0],$item,$item_id,null); + if(intval($item['item_wall']) || intval($item['item_origin']) || (! intval($item['item_thread_top'])) || ($item['id'] != $item['parent'])) { + logger('Item was local or a comment. rejected.'); + continue; + } + + logger('Creating second delivery chain.'); + start_delivery_chain($u[0],$item,$item_id,null); + + } + } } /** @@ -2760,78 +2761,73 @@ function tgroup_check($uid, $item) { if($terms) logger('tgroup_check: post mentions: ' . print_r($terms,true), LOGGER_DATA); + $max_forums = get_config('system','max_tagged_forums',2); + $matched_forums = 0; + $link = normalise_link($u[0]['xchan_url']); if($terms) { foreach($terms as $term) { - if(link_compare($term['url'],$link)) { - $mention = true; - break; + if(! link_compare($term['url'],$link)) { + continue; } - } - } - - if($mention) { - logger('tgroup_check: mention found for ' . $u[0]['channel_name']); - } - else - return false; - // At this point we've determined that the person receiving this post was mentioned in it. - // Now let's check if this mention was inside a reshare so we don't spam a forum - // note: $term has been set to the matching term + $mention = true; + logger('tgroup_check: mention found for ' . $u[0]['channel_name']); + // At this point we've determined that the person receiving this post was mentioned in it. + // Now let's check if this mention was inside a reshare so we don't spam a forum + // note: $term has been set to the matching term - $body = $item['body']; - $body = preg_replace('/\[share(.*?)\[\/share\]/','',$body); + $body = preg_replace('/\[share(.*?)\[\/share\]/','',$item['body']); - $pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; + $pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; - $forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/'; + $forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/'; + $found = false; - $found = false; + $matches = array(); - $max_forums = get_config('system','max_tagged_forums'); - if(! $max_forums) - $max_forums = 2; - $matched_forums = 0; - $matches = array(); - - if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) { - foreach($matches as $match) { - $matched_forums ++; - if($term['url'] === $match[1] && $term['term'] === $match[2]) { - if($matched_forums <= $max_forums) { - $found = true; - break; + if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) { + foreach($matches as $match) { + $matched_forums ++; + if($term['url'] === $match[1] && $term['term'] === $match[2] && intval($term['ttype']) === TERM_MENTION) { + if($matched_forums <= $max_forums) { + $found = true; + break; + } + logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); + } } - logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); } - } - } - if(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) { - foreach($matches as $match) { - $matched_forums ++; - if($term['url'] === $match[1] && $term['term'] === $match[2]) { - if($matched_forums <= $max_forums) { - $found = true; - break; + if(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) { + foreach($matches as $match) { + $matched_forums ++; + if($term['url'] === $match[1] && $term['term'] === $match[2] && intval($term['ttype']) === TERM_FORUM) { + if($matched_forums <= $max_forums) { + $found = true; + break; + } + logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); + } } - logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); } + + if(! $found) { + logger('tgroup_check: mention was in a reshare or exceeded max_tagged_forums - ignoring'); + continue; + } + + return true; } } - if(! $found) { - logger('tgroup_check: mention was in a reshare or exceeded max_tagged_forums - ignoring'); - return false; - } + return false; - return true; } /** -- cgit v1.2.3 From b12dc89bb1fbdc29f82fffee3587360d618ef603 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Sun, 12 Nov 2017 21:36:25 -0800 Subject: improve removed_channel final cleanup. Hubzilla issue #386 --- Zotlabs/Daemon/Cron.php | 2 +- Zotlabs/Daemon/Cron_weekly.php | 15 +++++++++++ include/channel.php | 60 +++++++++++++++++++++++++++++++----------- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 65edbedfa..01c43262a 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -78,7 +78,7 @@ class Cron { // channels and sites that quietly vanished and prevent the directory from accumulating stale // or dead entries. - $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s", + $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0", db_utcnow(), db_quoteinterval('30 DAY') ); diff --git a/Zotlabs/Daemon/Cron_weekly.php b/Zotlabs/Daemon/Cron_weekly.php index 5b185f475..d44400767 100644 --- a/Zotlabs/Daemon/Cron_weekly.php +++ b/Zotlabs/Daemon/Cron_weekly.php @@ -21,6 +21,21 @@ class Cron_weekly { mark_orphan_hubsxchans(); + // Find channels that were removed in the last three weeks, but + // haven't been finally cleaned up. These should be older than 10 + // days to ensure that "purgeall" messages have gone out or bounced + // or timed out. + + $r = q("select channel_id from channel where channel_removed = 1 and + channel_deleted > %s - INTERVAL %s and channel_deleted < %s - INTERVAL %s", + db_utcnow(), db_quoteinterval('21 DAY'), + db_utcnow(), db_quoteinterval('10 DAY') + ); + if($r) { + foreach($r as $rv) { + channel_remove_final($rv['channel_id']); + } + } // get rid of really old poco records diff --git a/include/channel.php b/include/channel.php index 6a6022aba..4f0e8ec6a 100644 --- a/include/channel.php +++ b/include/channel.php @@ -2527,19 +2527,43 @@ function channel_remove($channel_id, $local = true, $unset_session = false) { } } + q("DELETE FROM app WHERE app_channel = %d", intval($channel_id)); + q("DELETE FROM atoken WHERE atoken_uid = %d", intval($channel_id)); + q("DELETE FROM chatroom WHERE cr_uid = %d", intval($channel_id)); + q("DELETE FROM conv WHERE uid = %d", intval($channel_id)); q("DELETE FROM groups WHERE uid = %d", intval($channel_id)); q("DELETE FROM group_member WHERE uid = %d", intval($channel_id)); q("DELETE FROM event WHERE uid = %d", intval($channel_id)); - q("DELETE FROM item WHERE uid = %d", intval($channel_id)); q("DELETE FROM mail WHERE channel_id = %d", intval($channel_id)); + q("DELETE FROM menu WHERE menu_channel_id = %d", intval($channel_id)); + q("DELETE FROM menu_item WHERE mitem_channel_id = %d", intval($channel_id)); + q("DELETE FROM notify WHERE uid = %d", intval($channel_id)); + q("DELETE FROM obj WHERE obj_channel = %d", intval($channel_id)); + + q("DELETE FROM photo WHERE uid = %d", intval($channel_id)); q("DELETE FROM attach WHERE uid = %d", intval($channel_id)); q("DELETE FROM profile WHERE uid = %d", intval($channel_id)); - q("DELETE FROM pconfig WHERE uid = %d", intval($channel_id)); + q("DELETE FROM src WHERE src_channel_id = %d", intval($channel_id)); + + $r = q("select resource_id FROM attach WHERE uid = %d", intval($channel_id)); + if($r) { + foreach($r as $rv) { + attach_delete($channel_id,$rv['resource_id']); + } + } + + + + $r = q("select id from item where uid = %d", intval($channel_id)); + if($r) { + foreach($r as $rv) { + drop_item($rv['id'],false); + } + } - /// @FIXME At this stage we need to remove the file resources located under /store/$nickname q("delete from abook where abook_xchan = '%s' and abook_self = 1 ", dbesc($channel['channel_hash']) @@ -2593,19 +2617,11 @@ function channel_remove($channel_id, $local = true, $unset_session = false) { } //remove from file system - $r = q("select channel_address from channel where channel_id = %d limit 1", - intval($channel_id) - ); - if($r) { - $channel_address = $r[0]['channel_address'] ; - } - if($channel_address) { - $f = 'store/' . $channel_address.'/'; - logger('delete '. $f); - if(is_dir($f)) { - @rrmdir($f); - } + + $f = 'store/' . $channel['channel_address']; + if(is_dir($f)) { + @rrmdir($f); } Zotlabs\Daemon\Master::Summon(array('Directory',$channel_id)); @@ -2616,6 +2632,20 @@ function channel_remove($channel_id, $local = true, $unset_session = false) { } } +// execute this at least a week after removing a channel + +function channel_remove_final($channel_id) { + + q("delete from abook where abook_channel = %d", intval($channel_id)); + q("delete from abconfig where chan = %d", intval($channel_id)); + q("delete from pconfig where uid = %d", intval($channel_id)); + + +} + + + + /** * @brief This checks if a channel is allowed to publish executable code. * -- cgit v1.2.3 From 7b6ddeb859288a87573b922dcce086de21189f8c Mon Sep 17 00:00:00 2001 From: zotlabs Date: Sun, 12 Nov 2017 21:43:46 -0800 Subject: allow a site to block (public) the directory separately from other resources. --- Zotlabs/Module/Directory.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index 256667ef3..b1552a694 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -64,6 +64,11 @@ class Directory extends \Zotlabs\Web\Controller { return; } + if(get_config('system','block_public_directory',false) && (! get_observer_hash())) { + notice( t('Public access denied.') . EOL); + return; + } + $observer = get_observer_hash(); $globaldir = get_directory_setting($observer, 'globaldir'); -- cgit v1.2.3 From 217a684e5f0399683b191e7fa9b1a3781fb21196 Mon Sep 17 00:00:00 2001 From: Mario Vavti Date: Mon, 13 Nov 2017 10:08:51 +0100 Subject: also filter atokens on acl search --- Zotlabs/Module/Acl.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index e164875e8..ad1c8b8cd 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -176,11 +176,18 @@ class Acl extends \Zotlabs\Web\Controller { $extra_channels_sql = " OR (abook_channel IN ($extra_channels_sql)) and abook_hidden = 0 "; - // Add atokens belonging to the local channel @TODO restrict by search + // Add atokens belonging to the local channel + + if($search) { + $sql_extra_atoken = "AND ( atoken_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . ") "; + } + else { + $sql_extra_atoken = ''; + } $r2 = null; - $r1 = q("select * from atoken where atoken_uid = %d", + $r1 = q("select * from atoken where atoken_uid = %d $sql_extra_atoken", intval(local_channel()) ); -- cgit v1.2.3 From 5dad44402649db9844d735800300d86a8479b3fa Mon Sep 17 00:00:00 2001 From: Mario Vavti Date: Mon, 13 Nov 2017 19:55:09 +0100 Subject: fix php warnings on photo delete --- include/attach.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/attach.php b/include/attach.php index 179a57a90..2bb57722b 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1366,7 +1366,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { return; } - $url = get_cloudpath($channel_id, $channel_address, $resource); + $url = get_cloud_url($channel_id, $channel_address, $resource); $object = get_file_activity_object($channel_id, $resource, $url); // If resource is a directory delete everything in the directory recursive -- cgit v1.2.3 From c8dbcf8a2af7ac3b1a9e26c4c293574f919bd554 Mon Sep 17 00:00:00 2001 From: Mario Vavti Date: Mon, 13 Nov 2017 22:01:37 +0100 Subject: sort combined private mail conversations by latest updated conversation instead of created parent --- Zotlabs/Widget/Conversations.php | 12 +++++++----- include/message.php | 5 +---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Zotlabs/Widget/Conversations.php b/Zotlabs/Widget/Conversations.php index 56510750f..267d50fa0 100644 --- a/Zotlabs/Widget/Conversations.php +++ b/Zotlabs/Widget/Conversations.php @@ -28,6 +28,8 @@ class Conversations { require_once('include/message.php'); + $o = ''; + // private_messages_list() can do other more complicated stuff, for now keep it simple $r = private_messages_list(local_channel(), $mailbox, \App::$pager['start'], \App::$pager['itemspage']); @@ -36,13 +38,13 @@ class Conversations { return $o; } - $messages = array(); + $messages = []; foreach($r as $rr) { $selected = ((argc() == 3) ? intval(argv(2)) == intval($rr['id']) : $r[0]['id'] == $rr['id']); - $messages[] = array( + $messages[] = [ 'mailbox' => $mailbox, 'id' => $rr['id'], 'from_name' => $rr['from']['xchan_name'], @@ -57,14 +59,14 @@ class Conversations { 'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], 'c'), 'seen' => $rr['seen'], 'selected' => ((argv(1) != 'new') ? $selected : '') - ); + ]; } $tpl = get_markup_template('mail_head.tpl'); - $o .= replace_macros($tpl, array( + $o .= replace_macros($tpl, [ '$header' => $header, '$messages' => $messages - )); + ]); } return $o; diff --git a/include/message.php b/include/message.php index 477c7172c..b57d2e068 100644 --- a/include/message.php +++ b/include/message.php @@ -335,12 +335,9 @@ function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) { case 'combined': default: - - $parents = q("SELECT parent_mid FROM mail WHERE mid = parent_mid AND channel_id = %d ORDER BY created DESC", + $parents = q("SELECT mail.parent_mid FROM mail LEFT JOIN conv ON mail.conv_guid = conv.guid WHERE mail.mid = mail.parent_mid AND mail.channel_id = %d ORDER BY conv.updated DESC $limit", dbesc($local_channel) ); - //FIXME: We need the latest mail of a thread here. This query throws errors in postgres. We now look for the latest in php until somebody can fix this... - //$sql = "SELECT * FROM ( SELECT * FROM mail WHERE channel_id = $local_channel ORDER BY created DESC $limit ) AS temp_table GROUP BY parent_mid ORDER BY created DESC"; break; } -- cgit v1.2.3 From d5ff82bbe46fd2b42a6474f645749fc1ef56ceb2 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Mon, 13 Nov 2017 14:40:46 -0800 Subject: issue with group_rmv --- include/group.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/group.php b/include/group.php index e0c20b536..03ebf7ee5 100644 --- a/include/group.php +++ b/include/group.php @@ -18,10 +18,6 @@ function group_add($uid,$name,$public = 0) { intval($r) ); if(($z) && $z[0]['deleted']) { - /*$r = q("UPDATE groups SET deleted = 0 WHERE uid = %d AND gname = '%s'", - intval($uid), - dbesc($name) - );*/ q('UPDATE groups SET deleted = 0 WHERE id = %d', intval($z[0]['id'])); notice( t('A deleted group with this name was revived. Existing item permissions may apply to this group and any future members. If this is not what you intended, please create another group with a different name.') . EOL); } @@ -81,11 +77,11 @@ function group_rmv($uid,$name) { $user_info['channel_default_group'] = ''; $change = true; } - if(strpos($user_info['channel_allow_gid'], '<' . $group_id . '>') !== false) { + if(strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) { $user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']); $change = true; } - if(strpos($user_info['channel_deny_gid'], '<' . $group_id . '>') !== false) { + if(strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) { $user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']); $change = true; } -- cgit v1.2.3 From aff476d0d457f6debe21dfa1e2a0c3ccdcf71443 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Mon, 13 Nov 2017 15:21:49 -0800 Subject: move mailhost settings from plugin to core --- Zotlabs/Module/Settings/Channel.php | 4 ++++ view/tpl/settings.tpl | 3 +++ 2 files changed, 7 insertions(+) diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index 41e23b717..63370a141 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -148,6 +148,8 @@ class Channel { $defpermcat = ((x($_POST,'defpermcat')) ? notags(trim($_POST['defpermcat'])) : 'default'); $cal_first_day = (((x($_POST,'first_day')) && (intval($_POST['first_day']) == 1)) ? 1: 0); + $mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : ''); + $pageflags = $channel['channel_pageflags']; $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); @@ -239,6 +241,7 @@ class Channel { set_pconfig(local_channel(),'system','attach_path',$attach_path); set_pconfig(local_channel(),'system','cal_first_day',$cal_first_day); set_pconfig(local_channel(),'system','default_permcat',$defpermcat); + set_pconfig(local_channel(),'system','email_notify_host',$mailhost); $r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d", dbesc($username), @@ -561,6 +564,7 @@ class Channel { '$vnotify11' => array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no), '$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no), '$vnotify13' => ((get_config('system', 'disable_discover_tab') != 1) ? array('vnotify13', t('Unseen public activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no) : array()), + '$mailhost' => [ 'mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',\App::get_hostname()), sprintf( t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),\App::get_hostname()) ], '$always_show_in_notices' => array('always_show_in_notices', t('Also show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), diff --git a/view/tpl/settings.tpl b/view/tpl/settings.tpl index 704d89bdd..33e0aa925 100755 --- a/view/tpl/settings.tpl +++ b/view/tpl/settings.tpl @@ -104,6 +104,9 @@
+ + {{include file="field_input.tpl" field=$mailhost}} +

{{$activity_options}}

{{*not yet implemented *}} -- cgit v1.2.3 From 8d9c47acdf5644c6522cd4c0715891fab0a765dc Mon Sep 17 00:00:00 2001 From: Mario Vavti Date: Tue, 14 Nov 2017 22:35:58 +0100 Subject: move notifications full-screen handling to notifications widget --- view/theme/redbasic/js/redbasic.js | 14 -------------- view/tpl/notifications_widget.tpl | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/view/theme/redbasic/js/redbasic.js b/view/theme/redbasic/js/redbasic.js index 3fee0f282..ed9ef02aa 100644 --- a/view/theme/redbasic/js/redbasic.js +++ b/view/theme/redbasic/js/redbasic.js @@ -2,7 +2,6 @@ * redbasic theme specific JavaScript */ -var notifications_parent; $(document).ready(function() { // CSS3 calc() fallback (for unsupported browsers) @@ -84,19 +83,6 @@ $(document).ready(function() { } }); - notifications_parent = $('#notifications_wrapper')[0].parentElement.id; - $('#notifications-btn').click(function() { - if($('#notifications_wrapper').hasClass('fs')) - $('#notifications_wrapper').prependTo('#' + notifications_parent); - else - $('#notifications_wrapper').prependTo('section'); - - $('#notifications_wrapper').toggleClass('fs'); - if($('#navbar-collapse-2').hasClass('show')){ - $('#navbar-collapse-2').removeClass('show'); - } - }); - $("input[data-role=cat-tagsinput]").tagsinput({ tagClass: 'badge badge-pill badge-warning text-dark' }); diff --git a/view/tpl/notifications_widget.tpl b/view/tpl/notifications_widget.tpl index 0ece84891..29892ba79 100644 --- a/view/tpl/notifications_widget.tpl +++ b/view/tpl/notifications_widget.tpl @@ -28,6 +28,22 @@ {{if $module == 'display'}}