Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add contributors list #283

Merged
merged 1 commit into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:phpschool.team@gmail.com" 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;
}
}