implement community basics
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				rvr-nextgen/pipeline/pr-master This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	rvr-nextgen/pipeline/pr-master This commit looks good
				
			This commit is contained in:
		
							parent
							
								
									4dc08dffc9
								
							
						
					
					
						commit
						d763c4344c
					
				
							
								
								
									
										20
									
								
								database/migrations/structure/20230412_1955_communities.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								database/migrations/structure/20230412_1955_communities.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
CREATE TABLE `communities` (
 | 
			
		||||
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 | 
			
		||||
  `name` varchar(255) NOT NULL,
 | 
			
		||||
  `currency` varchar(3) NOT NULL,
 | 
			
		||||
  `created` timestamp NOT NULL DEFAULT current_timestamp(),
 | 
			
		||||
  PRIMARY KEY (`id`),
 | 
			
		||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
 | 
			
		||||
 | 
			
		||||
CREATE TABLE `community_members` (
 | 
			
		||||
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 | 
			
		||||
  `community_id` int(10) unsigned NOT NULL,
 | 
			
		||||
  `user_id` int(10) unsigned DEFAULT NULL,
 | 
			
		||||
  `owner` tinyint(1) NOT NULL DEFAULT 0,
 | 
			
		||||
  PRIMARY KEY (`id`),
 | 
			
		||||
  UNIQUE KEY `unique_user_for_community` (`community_id`, `user_id`),
 | 
			
		||||
  KEY `community_id` (`community_id`),
 | 
			
		||||
  KEY `user_id` (`user_id`),
 | 
			
		||||
  CONSTRAINT `community_members_community_id` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`),
 | 
			
		||||
  CONSTRAINT `community_members_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
 | 
			
		||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
 | 
			
		||||
@ -88,10 +88,14 @@ sub {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
hr {
 | 
			
		||||
    border: solid #bbbbbb 1px;
 | 
			
		||||
    border: solid #bbbbcc 1px;
 | 
			
		||||
    margin: 10px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.normal {
 | 
			
		||||
    font-weight: 400;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bold {
 | 
			
		||||
    font-weight: 700;
 | 
			
		||||
}
 | 
			
		||||
@ -418,13 +422,53 @@ div.buttonContainer>button {
 | 
			
		||||
 | 
			
		||||
div.box {
 | 
			
		||||
    width: 576px;
 | 
			
		||||
    background-color: #eeeeee;
 | 
			
		||||
    background-color: #eeeef4;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    margin: 10px auto;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.gridContainer {
 | 
			
		||||
    display: grid;
 | 
			
		||||
    grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
 | 
			
		||||
    grid-gap: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.gridContainer > div {
 | 
			
		||||
    background-color: #eeeef4;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    padding: 5px 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table {
 | 
			
		||||
    border-collapse: separate;
 | 
			
		||||
    border-spacing: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.fullWidth {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table th {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table th, table td {
 | 
			
		||||
    padding: 2px 0;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.choices__inner {
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media screen and (max-width: 424px) {
 | 
			
		||||
    div.gridContainer {
 | 
			
		||||
        grid-template-columns: auto;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media screen and (max-width: 599px) {
 | 
			
		||||
    header h1 span {
 | 
			
		||||
        display: none;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										62
									
								
								public/static/js/communities/community_members.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								public/static/js/communities/community_members.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
			
		||||
(function () {
 | 
			
		||||
    const element = document.getElementById('new_member_user_id');
 | 
			
		||||
    const choices = new Choices(element, {
 | 
			
		||||
        noResultsText: 'No users found',
 | 
			
		||||
        noChoicesText: 'Start typing to search users'
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    element.addEventListener('search', RVR.debounce(async function (e) {
 | 
			
		||||
        RVR.httpRequest('GET', searchUserUrl.replace('QUERY', encodeURIComponent(e.detail.value)), function () {
 | 
			
		||||
            choices.setChoices(this.response.results, 'value', 'label', true);
 | 
			
		||||
        });
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    element.addEventListener('choice', function () {
 | 
			
		||||
        choices.setChoices([], 'value', 'label', true);
 | 
			
		||||
        document.getElementById('new_member_button').disabled = false;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    document.getElementById('new_member_button').addEventListener('click', function () {
 | 
			
		||||
        document.getElementById('loading').style.visibility = 'visible';
 | 
			
		||||
 | 
			
		||||
        let data = new FormData();
 | 
			
		||||
        data.append('user_id', document.getElementById('new_member_user_id').value);
 | 
			
		||||
 | 
			
		||||
        RVR.httpRequest('POST', newMemberUrl, function () {
 | 
			
		||||
            window.location.reload();
 | 
			
		||||
        }, data);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const ownerCheckboxesButtons = document.getElementsByClassName('member_owner');
 | 
			
		||||
    for (const ownerCheckboxesButton of ownerCheckboxesButtons) {
 | 
			
		||||
        ownerCheckboxesButton.addEventListener('change', function () {
 | 
			
		||||
            document.getElementById('loading').style.visibility = 'visible';
 | 
			
		||||
 | 
			
		||||
            let data = new FormData();
 | 
			
		||||
            data.append('community_member_id', this.dataset.id);
 | 
			
		||||
            data.append('owner', this.checked ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
            RVR.httpRequest('POST', editMemberUrl, function () {
 | 
			
		||||
                document.getElementById('loading').style.visibility = 'hidden';
 | 
			
		||||
 | 
			
		||||
                if (!this.response.success) {
 | 
			
		||||
                    ownerCheckboxesButton.checked = !ownerCheckboxesButton.checked;
 | 
			
		||||
                }
 | 
			
		||||
            }, data);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const deleteButtons = document.getElementsByClassName('delete_member');
 | 
			
		||||
    for (const deleteButton of deleteButtons) {
 | 
			
		||||
        deleteButton.addEventListener('click', function () {
 | 
			
		||||
            document.getElementById('loading').style.visibility = 'visible';
 | 
			
		||||
 | 
			
		||||
            let data = new FormData();
 | 
			
		||||
            data.append('community_member_id', this.dataset.id);
 | 
			
		||||
 | 
			
		||||
            RVR.httpRequest('POST', deleteMemberUrl, function () {
 | 
			
		||||
                window.location.reload();
 | 
			
		||||
            }, data);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
})();
 | 
			
		||||
@ -193,6 +193,14 @@ var RVR = {
 | 
			
		||||
        form.onreset = function () {
 | 
			
		||||
            form.elements.submit.disabled = true;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    debounce: function(func, timeout = 300) {
 | 
			
		||||
        let timer;
 | 
			
		||||
        return (...args) => {
 | 
			
		||||
            clearTimeout(timer);
 | 
			
		||||
            timer = setTimeout(() => { func.apply(this, args); }, timeout);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								public/static/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								public/static/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
{
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "lockfileVersion": 1,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@babel/runtime": {
 | 
			
		||||
      "version": "7.21.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
 | 
			
		||||
      "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "regenerator-runtime": "^0.13.11"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "choices.js": {
 | 
			
		||||
      "version": "10.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-10.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-8PKy6wq7BMjNwDTZwr3+Zry6G2+opJaAJDDA/j3yxvqSCnvkKe7ZIFfIyOhoc7htIWFhsfzF9tJpGUATcpUtPg==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "deepmerge": "^4.2.2",
 | 
			
		||||
        "fuse.js": "^6.6.2",
 | 
			
		||||
        "redux": "^4.2.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "deepmerge": {
 | 
			
		||||
      "version": "4.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
 | 
			
		||||
      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
 | 
			
		||||
    },
 | 
			
		||||
    "fuse.js": {
 | 
			
		||||
      "version": "6.6.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz",
 | 
			
		||||
      "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA=="
 | 
			
		||||
    },
 | 
			
		||||
    "leaflet": {
 | 
			
		||||
      "version": "1.9.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
 | 
			
		||||
      "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "leaflet.markercluster": {
 | 
			
		||||
      "version": "1.5.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz",
 | 
			
		||||
      "integrity": "sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA=="
 | 
			
		||||
    },
 | 
			
		||||
    "redux": {
 | 
			
		||||
      "version": "4.2.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
 | 
			
		||||
      "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@babel/runtime": "^7.9.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "regenerator-runtime": {
 | 
			
		||||
      "version": "0.13.11",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
 | 
			
		||||
      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "leaflet": "^1.6.0",
 | 
			
		||||
    "leaflet.markercluster": "^1.4.1"
 | 
			
		||||
    "leaflet.markercluster": "^1.4.1",
 | 
			
		||||
    "choices.js": "^10.2.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										216
									
								
								src/Controller/CommunityController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/Controller/CommunityController.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,216 @@
 | 
			
		||||
<?php namespace RVR\Controller;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use RVR\PersistentData\Model\Community;
 | 
			
		||||
use RVR\PersistentData\Model\CommunityMember;
 | 
			
		||||
use RVR\PersistentData\Model\User;
 | 
			
		||||
use RVR\Repository\CommunityRepository;
 | 
			
		||||
use RVR\Repository\CommunityMemberRepository;
 | 
			
		||||
use RVR\Repository\UserRepository;
 | 
			
		||||
use SokoWeb\Interfaces\Authorization\ISecured;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
 | 
			
		||||
class CommunityController implements ISecured
 | 
			
		||||
{
 | 
			
		||||
    private IRequest $request;
 | 
			
		||||
 | 
			
		||||
    private PersistentDataManager $pdm;
 | 
			
		||||
 | 
			
		||||
    private UserRepository $userRepository;
 | 
			
		||||
 | 
			
		||||
    private CommunityRepository $communityRepository;
 | 
			
		||||
 | 
			
		||||
    private CommunityMemberRepository $communityMemberRepository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(IRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        $this->request = $request;
 | 
			
		||||
        $this->pdm = new PersistentDataManager();
 | 
			
		||||
        $this->userRepository = new UserRepository();
 | 
			
		||||
        $this->communityRepository = new CommunityRepository();
 | 
			
		||||
        $this->communityMemberRepository = new CommunityMemberRepository();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorize(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return $this->request->user() !== null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCommunityHome(): ?IContent
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->checkPermission($this->request->query('communityId'), false, $community, $ownCommunityMember)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('communities/community', [
 | 
			
		||||
            'community' => $community,
 | 
			
		||||
            'members' => $this->getMembers($community),
 | 
			
		||||
            'currencyNames' => [],
 | 
			
		||||
            'upcomingEvents' => [],
 | 
			
		||||
            'editPermission' => $ownCommunityMember->getOwner()
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCommunityNew(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        return new HtmlContent('communities/community_edit');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCommunityEdit(): ?IContent
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->checkPermission($this->request->query('communityId'), true, $community, $ownCommunityMember)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('communities/community_edit', [
 | 
			
		||||
            'community' => $community
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getMembersEdit(): ?IContent
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->checkPermission($this->request->query('communityId'), true, $community, $ownCommunityMember)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('communities/community_members', [
 | 
			
		||||
            'community' => $community,
 | 
			
		||||
            'members' => $this->getMembers($community)
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getMembers(Community $community): array
 | 
			
		||||
    {
 | 
			
		||||
        $members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true));
 | 
			
		||||
        usort($members, function($a, $b) {
 | 
			
		||||
            return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName());
 | 
			
		||||
        });
 | 
			
		||||
        return $members;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function newMember(): ?IContent
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->checkPermission($this->request->query('communityId'), true, $community, $ownCommunityMember)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = $this->userRepository->getById($this->request->post('user_id'));
 | 
			
		||||
 | 
			
		||||
        $communityMember = new CommunityMember();
 | 
			
		||||
        $communityMember->setCommunity($community);
 | 
			
		||||
        $communityMember->setUser($user);
 | 
			
		||||
        $this->pdm->saveToDb($communityMember);
 | 
			
		||||
 | 
			
		||||
        return new JsonContent(['success' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editMember(): ?IContent
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->checkPermission($this->request->query('communityId'), true, $community, $ownCommunityMember)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $communityMember = $this->communityMemberRepository->getById($this->request->post('community_member_id'));
 | 
			
		||||
        if ($communityMember->getUserId() === $this->request->user()->getUniqueId()) {
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => ['errorText' => 'Own user cannot be edited.']
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $communityMember->setOwner($this->request->post('owner'));
 | 
			
		||||
        $this->pdm->saveToDb($communityMember);
 | 
			
		||||
 | 
			
		||||
        return new JsonContent(['success' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteMember(): ?IContent
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->checkPermission($this->request->query('communityId'), true, $community, $ownCommunityMember)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $communityMember = $this->communityMemberRepository->getById($this->request->post('community_member_id'));
 | 
			
		||||
        if ($communityMember->getUserId() === $this->request->user()->getUniqueId()) {
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => ['errorText' => 'Own user cannot be deleted.']
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->pdm->deleteFromDb($communityMember);
 | 
			
		||||
 | 
			
		||||
        return new JsonContent(['success' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function saveCommunity(): ?IContent
 | 
			
		||||
    {
 | 
			
		||||
        $communityId = $this->request->query('communityId');
 | 
			
		||||
        if ($communityId){
 | 
			
		||||
            if (!$this->checkPermission($communityId, true, $community, $ownCommunityMember)) {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $community = new Community();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $name = $this->request->post('name');
 | 
			
		||||
        $currency = $this->request->post('currency');
 | 
			
		||||
        if (strlen($name) === 0 || strlen($currency) === 0 || strlen($currency) > 3) {
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => ['errorText' => 'Please fill all required fields!']
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $community->setName($name);
 | 
			
		||||
        $community->setCurrency($currency);
 | 
			
		||||
        if (!$communityId) {
 | 
			
		||||
            $community->setCreatedDate(new DateTime());
 | 
			
		||||
        }
 | 
			
		||||
        $this->pdm->saveToDb($community);
 | 
			
		||||
 | 
			
		||||
        if (!$communityId) {
 | 
			
		||||
            /**
 | 
			
		||||
            * @var User $user
 | 
			
		||||
            */
 | 
			
		||||
            $user = $this->request->user();
 | 
			
		||||
 | 
			
		||||
            $communityMember = new CommunityMember();
 | 
			
		||||
            $communityMember->setCommunity($community);
 | 
			
		||||
            $communityMember->setUser($user);
 | 
			
		||||
            $communityMember->setOwner(true);
 | 
			
		||||
            $this->pdm->saveToDb($communityMember);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new JsonContent([
 | 
			
		||||
            'redirect' => ['target' => '/' . \Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()])]
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function checkPermission(
 | 
			
		||||
        int $communityId,
 | 
			
		||||
        bool $needToBeOwner,
 | 
			
		||||
        ?Community &$community,
 | 
			
		||||
        ?CommunityMember &$ownCommunityMember): bool
 | 
			
		||||
    {
 | 
			
		||||
        $community = $this->communityRepository->getById($communityId);
 | 
			
		||||
        if ($community === null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
        * @var User $user
 | 
			
		||||
        */
 | 
			
		||||
        $user = $this->request->user();
 | 
			
		||||
 | 
			
		||||
        $ownCommunityMember = $this->communityMemberRepository->getByCommunityAndUser($community, $user);
 | 
			
		||||
        if ($ownCommunityMember === null || ($needToBeOwner && !$ownCommunityMember->getOwner())) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,18 +1,22 @@
 | 
			
		||||
<?php namespace RVR\Controller;
 | 
			
		||||
 | 
			
		||||
use RVR\PersistentData\Model\User;
 | 
			
		||||
use RVR\Repository\CommunityMemberRepository;
 | 
			
		||||
use SokoWeb\Interfaces\Authorization\ISecured;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
 | 
			
		||||
class HomeController implements ISecured
 | 
			
		||||
{
 | 
			
		||||
    private IRequest $request;
 | 
			
		||||
 | 
			
		||||
    private CommunityMemberRepository $communityMemberRepository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(IRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        $this->request = $request;
 | 
			
		||||
        $this->communityMemberRepository = new CommunityMemberRepository();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorize(): bool
 | 
			
		||||
@ -20,8 +24,25 @@ class HomeController implements ISecured
 | 
			
		||||
        return $this->request->user() !== null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getIndex(): IContent
 | 
			
		||||
    public function getHome(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        return new HtmlContent('index');
 | 
			
		||||
        /**
 | 
			
		||||
        * @var User $user
 | 
			
		||||
        */
 | 
			
		||||
        $user = $this->request->user();
 | 
			
		||||
 | 
			
		||||
        $ownCommunityMembers = $this->communityMemberRepository->getAllByUser($user, true);
 | 
			
		||||
        $communities = [];
 | 
			
		||||
        foreach ($ownCommunityMembers as $ownCommunityMember) {
 | 
			
		||||
            $communities[] = $ownCommunityMember->getCommunity();
 | 
			
		||||
        }
 | 
			
		||||
        usort($communities, function($a, $b) {
 | 
			
		||||
            return strnatcmp($a->getName(), $b->getName());
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('home', [
 | 
			
		||||
            'communities' => $communities,
 | 
			
		||||
            'upcomingEvents' => []
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										42
									
								
								src/Controller/UserSearchController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Controller/UserSearchController.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
<?php namespace RVR\Controller;
 | 
			
		||||
 | 
			
		||||
use RVR\Repository\UserRepository;
 | 
			
		||||
use SokoWeb\Interfaces\Authorization\ISecured;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
 | 
			
		||||
class UserSearchController implements ISecured
 | 
			
		||||
{
 | 
			
		||||
    private IRequest $request;
 | 
			
		||||
 | 
			
		||||
    private UserRepository $userRepository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(IRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        $this->request = $request;
 | 
			
		||||
        $this->userRepository = new UserRepository();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorize(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return $this->request->user() !== null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function searchUser(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        $users = iterator_to_array($this->userRepository->searchByName($this->request->query('q')));
 | 
			
		||||
        usort($users, function($a, $b) {
 | 
			
		||||
            return strnatcmp($a->getDisplayName(), $b->getDisplayName());
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        $results = [];
 | 
			
		||||
        foreach ($users as $user) {
 | 
			
		||||
            $results[] = ['value' => $user->getId(), 'label' => $user->getFullDisplayName()];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new JsonContent([
 | 
			
		||||
            'results' => $results
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								src/PersistentData/Model/Community.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/PersistentData/Model/Community.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
<?php namespace RVR\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class Community extends Model
 | 
			
		||||
{
 | 
			
		||||
    protected static string $table = 'communities';
 | 
			
		||||
 | 
			
		||||
    protected static array $fields = ['name', 'currency', 'created'];
 | 
			
		||||
 | 
			
		||||
    private string $name = '';
 | 
			
		||||
 | 
			
		||||
    private string $currency = '';
 | 
			
		||||
 | 
			
		||||
    private DateTime $created;
 | 
			
		||||
 | 
			
		||||
    public function setName(string $name): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setCurrency(string $currency): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->currency = $currency;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setCreatedDate(DateTime $created): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->created = $created;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setCreated(string $created): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->created = new DateTime($created);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getName(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCurrency(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->currency;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCreatedDate(): DateTime
 | 
			
		||||
    {
 | 
			
		||||
        return $this->created;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCreated(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->created->format('Y-m-d H:i:s');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								src/PersistentData/Model/CommunityMember.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/PersistentData/Model/CommunityMember.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
<?php namespace RVR\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class CommunityMember extends Model
 | 
			
		||||
{
 | 
			
		||||
    protected static string $table = 'community_members';
 | 
			
		||||
 | 
			
		||||
    protected static array $fields = ['community_id', 'user_id', 'owner'];
 | 
			
		||||
 | 
			
		||||
    protected static array $relations = ['community' => Community::class, 'user' => User::class];
 | 
			
		||||
 | 
			
		||||
    private ?Community $community = null;
 | 
			
		||||
 | 
			
		||||
    private ?int $communityId = null;
 | 
			
		||||
 | 
			
		||||
    private ?User $user = null;
 | 
			
		||||
 | 
			
		||||
    private ?int $userId = null;
 | 
			
		||||
 | 
			
		||||
    private bool $owner = false;
 | 
			
		||||
 | 
			
		||||
    public function setCommunity(Community $community): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->community = $community;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setCommunityId(int $communityId): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->communityId = $communityId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUser(User $user): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->user = $user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUserId(int $userId): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->userId = $userId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setOwner(bool $owner): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->owner = $owner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCommunity(): ?Community
 | 
			
		||||
    {
 | 
			
		||||
        return $this->community;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCommunityId(): ?int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->communityId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getUser(): ?User
 | 
			
		||||
    {
 | 
			
		||||
        return $this->user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getUserId(): ?int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->userId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getOwner(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return $this->owner;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -171,6 +171,11 @@ class User extends Model implements IUser
 | 
			
		||||
        return $this->nickname ?: $this->fullName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getFullDisplayName(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->nickname ? $this->nickname . ' (' . $this->fullName . ')' : $this->fullName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function checkPassword(string $password): bool
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->password === null) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								src/Repository/CommunityMemberRepository.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/Repository/CommunityMemberRepository.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
<?php namespace RVR\Repository;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use RVR\PersistentData\Model\Community;
 | 
			
		||||
use RVR\PersistentData\Model\CommunityMember;
 | 
			
		||||
use RVR\PersistentData\Model\User;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class CommunityMemberRepository
 | 
			
		||||
{
 | 
			
		||||
    private PersistentDataManager $pdm;
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        $this->pdm = new PersistentDataManager();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getById(int $id): ?CommunityMember
 | 
			
		||||
    {
 | 
			
		||||
        return $this->pdm->selectFromDbById($id, CommunityMember::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAllByCommunity(Community $community, bool $useRelations = false): Generator
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->where('community_id', '=', $community->getId());
 | 
			
		||||
 | 
			
		||||
        yield from $this->pdm->selectMultipleFromDb($select, CommunityMember::class, $useRelations);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAllByUser(User $user, bool $useRelations = false): Generator
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->where('user_id', '=', $user->getId());
 | 
			
		||||
 | 
			
		||||
        yield from $this->pdm->selectMultipleFromDb($select, CommunityMember::class, $useRelations);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getByCommunityAndUser(Community $community, User $user) : ?CommunityMember
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->where('community_id', '=', $community->getId());
 | 
			
		||||
        $select->where('user_id', '=', $user->getId());
 | 
			
		||||
 | 
			
		||||
        return $this->pdm->selectFromDb($select, CommunityMember::class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/Repository/CommunityRepository.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Repository/CommunityRepository.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
<?php namespace RVR\Repository;
 | 
			
		||||
 | 
			
		||||
use RVR\PersistentData\Model\Community;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class CommunityRepository
 | 
			
		||||
{
 | 
			
		||||
    private PersistentDataManager $pdm;
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        $this->pdm = new PersistentDataManager();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getById(int $id): ?Community
 | 
			
		||||
    {
 | 
			
		||||
        return $this->pdm->selectFromDbById($id, Community::class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<?php namespace RVR\Repository;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use SokoWeb\Interfaces\Repository\IUserRepository;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use RVR\PersistentData\Model\User;
 | 
			
		||||
@ -51,4 +52,14 @@ class UserRepository implements IUserRepository
 | 
			
		||||
 | 
			
		||||
        return $this->pdm->selectFromDb($select, User::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function searchByName(string $name): Generator
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->where('full_name', 'LIKE', '%' . $name . '%');
 | 
			
		||||
        $select->orWhere('nickname', 'LIKE', '%' . $name . '%');
 | 
			
		||||
        $select->limit(10);
 | 
			
		||||
 | 
			
		||||
        yield from $this->pdm->selectMultipleFromDb($select, User::class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								views/communities/community.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								views/communities/community.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
@extends(templates/layout_normal)
 | 
			
		||||
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2><?= $community->getName() ?> <span class="small">[<a href="/<?= Container::$routeCollection->getRoute('community-edit')->generateLink(['communityId' => $community->getId()]) ?>">edit</a>]</span></h2>
 | 
			
		||||
 | 
			
		||||
    <div class="gridContainer marginTop">
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3 class="marginBottom">Members</h3>
 | 
			
		||||
            <?php foreach ($members as $member): ?>
 | 
			
		||||
                <p><?= $member->getUser()->getDisplayName() ?></p>
 | 
			
		||||
            <?php endforeach; ?>
 | 
			
		||||
            <?php if ($editPermission): ?>
 | 
			
		||||
                <hr>
 | 
			
		||||
                <p><a href="/<?= Container::$routeCollection->getRoute('community-members')->generateLink(['communityId' => $community->getId()]) ?>">Edit members</a></p>
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3 class="marginBottom">Currencies</h3>
 | 
			
		||||
            <p>Main currency: <b><?= $community->getCurrency() ?></b></p>
 | 
			
		||||
            <p>Further currencies: <b><?= implode(', ', $currencyNames) ?></b></p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3 class="marginBottom">Upcoming events</h3>
 | 
			
		||||
            <?php if (count($upcomingEvents) > 0): ?>
 | 
			
		||||
                <?php foreach ($upcomingEvents as $event): ?>
 | 
			
		||||
                    <!-- todo -->
 | 
			
		||||
                <?php endforeach; ?>
 | 
			
		||||
            <?php else: ?>
 | 
			
		||||
                <p>There is no upcoming event.</p>
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3 class="marginBottom">Finances</h3>
 | 
			
		||||
            <table class="fullWidth">
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>You owe</td>
 | 
			
		||||
                    <td style="text-align: right; color: red;">0 <?= $community->getCurrency() ?></td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>You're owed</td>
 | 
			
		||||
                    <td style="text-align: right; color: green;">0 <?= $community->getCurrency() ?></td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>Your balance</td>
 | 
			
		||||
                    <td style="text-align: right;">0 <?= $community->getCurrency() ?></td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            </table>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
@endsection
 | 
			
		||||
							
								
								
									
										20
									
								
								views/communities/community_edit.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								views/communities/community_edit.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
@extends(templates/layout_normal)
 | 
			
		||||
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2><?= isset($community) ? $community->getName() . ' - Edit' : 'New community' ?></h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <?php
 | 
			
		||||
        $formAction = isset($community) ?
 | 
			
		||||
            Container::$routeCollection->getRoute('community-edit-action')->generateLink(['communityId' => $community->getId()]) :
 | 
			
		||||
            Container::$routeCollection->getRoute('community-new-action')->generateLink();
 | 
			
		||||
        ?>
 | 
			
		||||
        <form id="communityForm" action="/<?= $formAction ?>" method="post" data-redirect-on-success="true">
 | 
			
		||||
            <input type="text" class="text big fullWidth" name="name" placeholder="Name" value="<?= isset($community) ? $community->getName() : '' ?>" required>
 | 
			
		||||
            <input type="text" class="text big fullWidth marginTop" name="currency" value="<?= isset($community) ? $community->getCurrency() : '' ?>" placeholder="Default currency" maxlength="3" required>
 | 
			
		||||
            <p id="accountFormError" class="formError justify marginTop"></p>
 | 
			
		||||
            <div class="right marginTop">
 | 
			
		||||
                <button type="submit" name="submit"><?= isset($community) ? 'Save' : 'Create' ?></button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
    </div>
 | 
			
		||||
@endsection
 | 
			
		||||
							
								
								
									
										42
									
								
								views/communities/community_members.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								views/communities/community_members.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
@css(node_modules/choices.js/public/assets/styles/choices.min.css)
 | 
			
		||||
@js(node_modules/choices.js/public/assets/scripts/choices.js)
 | 
			
		||||
@js(js/communities/community_members.js)
 | 
			
		||||
 | 
			
		||||
@extends(templates/layout_normal)
 | 
			
		||||
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2><?= $community->getName() ?> - Edit members</h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <table class="fullWidth">
 | 
			
		||||
            <thead>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th style="width: 50%;"></th>
 | 
			
		||||
                    <th style="width: 25%; text-align: center;">Owner</th>
 | 
			
		||||
                    <th style="width: 25%;"></th>
 | 
			
		||||
                </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <?php foreach ($members as $member): ?>
 | 
			
		||||
                <?php $editable = $member->getUserId() !== Container::$request->user()->getUniqueId(); ?>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td><?= $member->getUser()->getDisplayName() ?></td>
 | 
			
		||||
                    <td style="text-align: center;"><input type="checkbox" class="member_owner" data-id="<?= $member->getId() ?>" <?= $member->getOwner() ? 'checked' : '' ?> <?= !$editable ? 'disabled' : '' ?> /></td>
 | 
			
		||||
                    <td style="text-align: right;"><button type="button" class="small red delete_member" data-id="<?= $member->getId() ?>" <?= !$editable ? 'disabled' : '' ?>>Delete</button></td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            <?php endforeach; ?>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td><select type="text" id="new_member_user_id"></td>
 | 
			
		||||
                <td></td>
 | 
			
		||||
                <td style="text-align: right;"><button type="button" class="small" id="new_member_button" disabled>Add</button></td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </table>
 | 
			
		||||
    </div>
 | 
			
		||||
@endsection
 | 
			
		||||
 | 
			
		||||
@section(pageScript)
 | 
			
		||||
<script>
 | 
			
		||||
    var searchUserUrl = '/<?= Container::$routeCollection->getRoute('searchUser')->generateLink(['q' => 'QUERY']) ?>';
 | 
			
		||||
    var newMemberUrl = '/<?= Container::$routeCollection->getRoute('community-members-new')->generateLink(['communityId' => $community->getId()]) ?>';
 | 
			
		||||
    var editMemberUrl = '/<?= Container::$routeCollection->getRoute('community-members-edit')->generateLink(['communityId' => $community->getId()]) ?>';
 | 
			
		||||
    var deleteMemberUrl = '/<?= Container::$routeCollection->getRoute('community-members-delete')->generateLink(['communityId' => $community->getId()]) ?>';
 | 
			
		||||
</script>
 | 
			
		||||
@endsection
 | 
			
		||||
							
								
								
									
										28
									
								
								views/home.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								views/home.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
@extends(templates/layout_normal)
 | 
			
		||||
 | 
			
		||||
@section(main)
 | 
			
		||||
    <div class="gridContainer">
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3 class="marginBottom">Communities</h3>
 | 
			
		||||
            <?php if (count($communities) > 0): ?>
 | 
			
		||||
                <?php foreach ($communities as $community): ?>
 | 
			
		||||
                    <p><a href="/<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a></p>
 | 
			
		||||
                <?php endforeach; ?>
 | 
			
		||||
            <?php else: ?>
 | 
			
		||||
                <p>You have no community.</p>
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
            <hr>
 | 
			
		||||
            <p><a href="/<?= Container::$routeCollection->getRoute('community-new')->generateLink() ?>">New community</a></p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3 class="marginBottom">Upcoming events</h3>
 | 
			
		||||
            <?php if (count($upcomingEvents) > 0): ?>
 | 
			
		||||
                <?php foreach ($upcomingEvents as $event): ?>
 | 
			
		||||
                    <!-- todo -->
 | 
			
		||||
                <?php endforeach; ?>
 | 
			
		||||
            <?php else: ?>
 | 
			
		||||
                <p>There is no upcoming event.</p>
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
@endsection
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
@extends(templates/layout_normal)
 | 
			
		||||
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2><?= $_ENV['APP_NAME'] ?></h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <p>This is the new <?= $_ENV['APP_NAME'] ?>.</p>
 | 
			
		||||
    </div>
 | 
			
		||||
@endsection
 | 
			
		||||
							
								
								
									
										16
									
								
								web.php
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								web.php
									
									
									
									
									
								
							@ -12,7 +12,7 @@ if (!empty($_ENV['DEV'])) {
 | 
			
		||||
 | 
			
		||||
Container::$routeCollection = new SokoWeb\Routing\RouteCollection();
 | 
			
		||||
 | 
			
		||||
Container::$routeCollection->get('index', '', [RVR\Controller\HomeController::class, 'getIndex']);
 | 
			
		||||
Container::$routeCollection->get('home', '', [RVR\Controller\HomeController::class, 'getHome']);
 | 
			
		||||
Container::$routeCollection->get('startSession', 'startSession.json', [RVR\Controller\HomeController::class, 'startSession']);
 | 
			
		||||
Container::$routeCollection->group('login', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('login', '', [RVR\Controller\LoginController::class, 'getLoginForm']);
 | 
			
		||||
@ -41,6 +41,20 @@ Container::$routeCollection->group('account', function (SokoWeb\Routing\RouteCol
 | 
			
		||||
    $routeCollection->get('account.googleAuthenticate', 'googleAuthenticate', [RVR\Controller\UserController::class, 'getGoogleAuthenticateRedirect']);
 | 
			
		||||
    $routeCollection->get('account.googleAuthenticate-action', 'googleAuthenticate/code', [RVR\Controller\UserController::class, 'authenticateWithGoogle']);
 | 
			
		||||
});
 | 
			
		||||
Container::$routeCollection->get('searchUser', 'searchUser', [RVR\Controller\UserSearchController::class, 'searchUser']);
 | 
			
		||||
Container::$routeCollection->group('communities', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('community-new', 'new', [RVR\Controller\CommunityController::class, 'getCommunityNew']);
 | 
			
		||||
    $routeCollection->post('community-new-action', 'new', [RVR\Controller\CommunityController::class, 'saveCommunity']);
 | 
			
		||||
    $routeCollection->group('{communityId}', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
        $routeCollection->get('community', '', [RVR\Controller\CommunityController::class, 'getCommunityHome']);
 | 
			
		||||
        $routeCollection->get('community-edit', 'edit', [RVR\Controller\CommunityController::class, 'getCommunityEdit']);
 | 
			
		||||
        $routeCollection->post('community-edit-action', 'edit', [RVR\Controller\CommunityController::class, 'saveCommunity']);
 | 
			
		||||
        $routeCollection->get('community-members', 'members', [RVR\Controller\CommunityController::class, 'getMembersEdit']);
 | 
			
		||||
        $routeCollection->post('community-members-new', 'newMember', [RVR\Controller\CommunityController::class, 'newMember']);
 | 
			
		||||
        $routeCollection->post('community-members-edit', 'editMember', [RVR\Controller\CommunityController::class, 'editMember']);
 | 
			
		||||
        $routeCollection->post('community-members-delete', 'deleteMember', [RVR\Controller\CommunityController::class, 'deleteMember']);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Container::$sessionHandler = new SokoWeb\Session\DatabaseSessionHandler();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user