Skip to content

Commit

Permalink
Merge pull request #283 from php-school/contributors
Browse files Browse the repository at this point in the history
Add contributors list
  • Loading branch information
AydinHassan authored Mar 11, 2024
2 parents ba42824 + d698a19 commit 55bcb24
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 2 deletions.
1 change: 1 addition & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ CACHE.ENABLE=false
DISPLAY_ERRORS=true
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_TOKEN=
DEV_MODE=true
14 changes: 14 additions & 0 deletions app/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Doctrine\ORM\ORMSetup;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Github\AuthMethod;
use Github\Client;
use League\CommonMark\Extension\CommonMarkCoreExtension;
use League\CommonMark\Extension\ExternalLink\ExternalLinkExtension;
Expand Down Expand Up @@ -50,6 +51,7 @@
use PhpSchool\Website\Action\SubmitWorkshop;
use PhpSchool\Website\Action\TrackDownloads;
use PhpSchool\Website\Blog\Generator;
use PhpSchool\Website\Command\SyncContributors;
use PhpSchool\Website\Online\CloudWorkshopRepository;
use PhpSchool\Website\Online\Command\DownloadComposerPackageList;
use PhpSchool\Website\Online\Middleware\ExerciseRunnerRateLimiter;
Expand Down Expand Up @@ -112,6 +114,7 @@
$app->command('create-admin-user name email password', CreateAdminUser::class);
$app->command('generate-blog', GenerateBlog::class);
$app->command('download-composer-packages', DownloadComposerPackageList::class);
$app->command('sync-contributors', SyncContributors::class);

ConsoleRunner::addCommands($app, new SingleManagerProvider($c->get(EntityManagerInterface::class)));

Expand Down Expand Up @@ -189,6 +192,9 @@
DownloadComposerPackageList::class => function (ContainerInterface $c): DownloadComposerPackageList {
return new DownloadComposerPackageList($c->get('guzzle.packagist'), $c->get(LoggerInterface::class));
},
SyncContributors::class => function (ContainerInterface $c): SyncContributors {
return new SyncContributors($c->get(Client::class), $c->get(LoggerInterface::class));
},

TrackDownloads::class => function (ContainerInterface $c): TrackDownloads {
return new TrackDownloads($c->get(WorkshopRepository::class), $c->get(WorkshopInstallRepository::class));
Expand All @@ -212,6 +218,13 @@
);
},

Client::class => function (ContainerInterface $c): Client {
$client = new Client();
$client->authenticate($c->get('config')['github']['token'], AuthMethod::ACCESS_TOKEN);

return $client;
},

Github::class => function (ContainerInterface $c): Github {
return new Github([
'clientId' => $c->get('config')['github']['clientId'],
Expand Down Expand Up @@ -582,6 +595,7 @@ public function parse($markdown): string
'github' => [
'clientId' => $_ENV['GITHUB_CLIENT_ID'],
'clientSecret' => $_ENV['GITHUB_CLIENT_SECRET'],
'token' => $_ENV['GITHUB_TOKEN'],
],

'jwtSecret' => $_ENV['JWT_SECRET'],
Expand Down
58 changes: 58 additions & 0 deletions assets/components/Website/ProjectContributors.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script setup>
import { ChatBubbleLeftRightIcon } from "@heroicons/vue/24/solid";
import Modal from "../Online/ModalDialog.vue";
import { onMounted, ref } from "vue";
const { open } = defineProps({
open: Boolean,
});
const emit = defineEmits(["close"]);
const contributors = ref([]);
onMounted(async () => {
const response = await fetch("/api/contributors");
contributors.value = await response.json();
});
</script>

<template>
<Teleport to="body">
<Transition
enter-active-class="transition-opacity duration-100 ease-in"
leave-active-class="transition-opacity duration-200 ease-in"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<Modal :scroll-content="true" size="md" max-height="max-h-[calc(5/6*100%)]" v-if="open" @close="emit('close')">
<template #header>
<div class="flex items-center">
<ChatBubbleLeftRightIcon class="mr-2 h-6 w-6 text-pink-500" />
<h3 class="mt-0 pt-0 font-mono text-base font-semibold text-white lg:text-xl">Contributors</h3>
</div>
</template>
<template #body>
<p class="mb-4 text-justify text-sm italic text-white">The PHP School project is made possible by the following contributors. Thank you for your hard work and dedication to the project!</p>
<ul role="list" class="divide-y divide-gray-800">
<li v-for="contributor in contributors" :key="contributor.username" class="flex justify-between gap-x-6 py-2">
<div class="flex min-w-0 gap-x-3">
<img class="h-8 w-8 flex-none rounded-full bg-gray-800" :src="contributor.profilePic" alt="" />
<div class="min-w-0 flex-auto">
<a :href="contributor.profile" target="_blank" class="text-sm leading-6 text-white">{{ contributor.username }}</a>
</div>
</div>
<div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
<p class="mt-1 text-xs leading-5 text-pink-500">
<b>{{ contributor.contributions }}</b>
Commit{{ contributor.contributions > 1 ? "s" : "" }}
</p>
</div>
</li>
</ul>
</template>
</Modal>
</Transition>
</Teleport>
</template>
5 changes: 4 additions & 1 deletion assets/components/Website/SiteFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import GitHubIcon from "../Icons/GitHubIcon.vue";
import Logo from "./SiteLogo.vue";
import { ref } from "vue";
import JoinSlack from "./JoinSlack.vue";
import Contributors from "./ProjectContributors.vue";
const currentYear = new Date().getFullYear();
const slackModalOpen = ref(false);
const contributorModalOpen = ref(false);
</script>
<template>
<footer class="bg-cyan-600 font-sans text-sm text-white">
Expand Down Expand Up @@ -37,7 +39,7 @@ const slackModalOpen = ref(false);
<div class="mb-4 ml-0 flex-grow sm:mb-0 sm:ml-6">
<h4 class="mb-2 font-work-sans text-lg font-bold capitalize not-italic text-white">The Creators</h4>
<ul class="list-none">
<li class="py-1"><router-link to="/events" class="decoration-pink-600 decoration-2 underline-offset-4 hover:underline">About Us</router-link></li>
<li class="py-1"><span @click="contributorModalOpen = true" class="cursor-pointer decoration-pink-600 decoration-2 underline-offset-4 hover:underline">Contributors</span></li>
<li class="py-1"><a href="mailto:[email protected]" class="decoration-pink-600 decoration-2 underline-offset-4 hover:underline">Email Us</a></li>
<li class="py-1"><a href="https://github.com/php-school" target="_blank" class="decoration-pink-600 decoration-2 underline-offset-4 hover:underline">GitHub</a></li>
</ul>
Expand Down Expand Up @@ -67,5 +69,6 @@ const slackModalOpen = ref(false);
</div>
</div>
<JoinSlack :open="slackModalOpen" @close="slackModalOpen = false" />
<Contributors v-if="contributorModalOpen" :open="contributorModalOpen" @close="contributorModalOpen = false" />
</footer>
</template>
2 changes: 2 additions & 0 deletions public/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use PhpSchool\Website\Action\Admin\Workshop\View;
use PhpSchool\Website\Action\BlogPost;
use PhpSchool\Website\Action\BlogPosts;
use PhpSchool\Website\Action\Contributors;
use PhpSchool\Website\Action\Events;
use PhpSchool\Website\Action\Online\ComposerPackageAdd;
use PhpSchool\Website\Action\Online\ComposerPackageSearch;
Expand Down Expand Up @@ -69,6 +70,7 @@
$app->get('/api/events', Events::class);
$app->get('/api/posts', BlogPosts::class);
$app->post('/api/slack-invite', SlackInvite::class);
$app->get('/api/contributors', Contributors::class);

$app
->group('/api/admin', function (RouteCollectorProxy $group) {
Expand Down
19 changes: 19 additions & 0 deletions src/Action/Contributors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace PhpSchool\Website\Action;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class Contributors
{
use JsonUtils;
public function __invoke(Request $request, Response $response): Response
{
/** @var array<string, array{username: string, contributions: int, profilePic: string, profile: string}> $data */
$data = json_decode((string) file_get_contents(__DIR__ . '/../../var/contributors.json'));
return $this->withJson($data, $response);
}
}
2 changes: 1 addition & 1 deletion src/Action/JsonUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ private function jsonSuccess(Response $response): Response
}

/**
* @param array<string, mixed> $json
* @param array<string|int, mixed> $json
*/
private function withJson(array $json, Response $response, int $status = 200): Response
{
Expand Down
69 changes: 69 additions & 0 deletions src/Command/SyncContributors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace PhpSchool\Website\Command;

use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Output\OutputInterface;

class SyncContributors
{
private const CONTRIBUTORS_FILE_LOCATION = __DIR__ . '/../../var/contributors.json';

public function __construct(private \Github\Client $client, private LoggerInterface $logger) {}

public function __invoke(OutputInterface $output): void
{
try {
$contributors = $this->downloadContributors();
} catch (\Exception $e) {
$this->logger->error('Could not download contributors. Error: ' . $e->getMessage());
return;
}

file_put_contents(self::CONTRIBUTORS_FILE_LOCATION, json_encode($contributors, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));

$output->writeln('<info>Contributors synced</info>');
}

/**
* @return array<int, array{
* username: string,
* contributions: int,
* profilePic: string,
* profile: string
* }>
*/
private function downloadContributors(): array
{
$repositories = $this->client->user()->repositories('php-school');

$contributors = [];

foreach ($repositories as $repository) {
$result = $this->client->repositories()->contributors('php-school', $repository['name']);

if (!is_array($result)) {
continue;
}

foreach ($result as $contributor) {
if (!isset($contributors[$contributor['login']])) {
$contributors[$contributor['login']] = [
'username' => $contributor['login'],
'contributions' => (int) $contributor['contributions'],
'profilePic' => $contributor['avatar_url'],
'profile' => $contributor['html_url'],
];
} else {
$contributors[$contributor['login']]['contributions'] += (int) $contributor['contributions'];
}
}
}

usort($contributors, fn(array $a, array $b) => $b['contributions'] <=> $a['contributions']);

return $contributors;
}
}

0 comments on commit 55bcb24

Please sign in to comment.