PluginProbe
Github Embed / 1.6
Github Embed v1.6
trunk 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.0.1 2.0.2 2.1.0 2.2.0 2.2.1
github-embed / github-embed.php

github-embed.php in Github Embed 1.6, at github-embed.php

338 lines 12.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 Plugin Name: Github Embed
5 Plugin URI: http://www.leewillis.co.uk/wordpress-plugins
6 Description: Paste the URL to a Github project into your posts or pages, and have the project information pulled in and displayed automatically
7 Version: 1.6
8 Author: Lee Willis
9 Author URI: http://www.leewillis.co.uk/
10 */
11
12 /**
13 * Copyright (c) 2013 Lee Willis. All rights reserved.
14 *
15 * Released under the GPL license
16 * http://www.opensource.org/licenses/gpl-license.php
17 *
18 * This is an add-on for WordPress
19 * http://wordpress.org/
20 *
21 * **********************************************************************
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 * **********************************************************************
32 */
33
34 /**
35 * This class handles being the oEmbed provider in terms of registering the URLs that
36 * we can embed, and handling the actual oEmbed calls. It relies on the github_api
37 * class to retrieve the information from the GitHub API.
38 * @uses class github_api
39 */
40
41 class github_embed {
42
43 private $api;
44
45 /**
46 * Constructor. Registers hooks and filters
47 * @param class $api An instance of the github_api classs
48 */
49 public function __construct( $api ) {
50 $this->api = $api;
51 add_action( 'init', array( $this, 'register_oembed_handler' ) );
52 add_action( 'init', array( $this, 'maybe_handle_oembed' ) );
53 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
54 add_action( 'admin_init', array( $this, 'schedule_expiry' ) );
55 add_action( 'github_embed_cron', array( $this, 'cron' ) );
56 // @TODO i18n
57 }
58
59 /**
60 * Make sure we have a scheduled event set to clear down the oEmbed cache until
61 * WordPress supports cache_age in oEmbed responses.
62 */
63 function schedule_expiry() {
64 if ( ! wp_next_scheduled( 'github_embed_cron' ) ) {
65 $frequency = apply_filters( 'github_embed_cache_frequency', 'daily' );
66 wp_schedule_event( time(), $frequency, 'github_embed_cron' );
67 }
68 }
69
70 /**
71 * Expire old oEmbeds.
72 * Note: This is a bit sledgehammer-to-crack-a-nut hence why I'm only running it
73 * daily. Ideally WP should honour cache_age in oEmbed responses properly
74 */
75 function cron() {
76 global $wpdb, $table_prefix;
77 $sql = "DELETE
78 FROM {$table_prefix}postmeta
79 WHERE meta_key LIKE '_oembed_%'";
80 $results = $wpdb->get_results( $sql );
81 }
82
83 /**
84 * Enqueue the frontend CSS
85 * @return void
86 */
87 function enqueue_styles() {
88 wp_register_style( 'github-embed', plugins_url( basename( dirname( __FILE__ ) ) . '/css/github-embed.css' ) );
89 wp_enqueue_style( 'github-embed' );
90 }
91
92 /**
93 * Register the oEmbed provider, and point it at a local endpoint since github
94 * doesn't directly support oEmbed yet. Our local endpoint will use the github
95 * API to fulfil the request.
96 * @param array $providers The current list of providers
97 * @return array The list, with our new provider added
98 */
99 public function register_oembed_handler() {
100 $oembed_url = home_url();
101 $key = $this->get_key();
102 $oembed_url = add_query_arg( array( 'github_oembed' => $key ), $oembed_url );
103 wp_oembed_add_provider( '#https?://github.com/.*#i', $oembed_url, true );
104 }
105
106 /**
107 * Generate a unique key that can be used on our requests to stop others
108 * hijacking our internal oEmbed API
109 * @return string The site key
110 */
111 private function get_key() {
112 $key = get_option( 'github_oembed_key' );
113 if ( ! $key ) {
114 $key = md5( time() . rand( 0, 65535 ) );
115 add_option( 'github_oembed_key', $key, '', 'yes' );
116 }
117 return $key;
118 }
119
120 /**
121 * Check whether this is an oembed request, handle if it is
122 * Ignore it if not.
123 * Insert rant here about WP's lack of a front-end AJAX handler.
124 */
125 public function maybe_handle_oembed() {
126 if ( isset( $_GET['github_oembed'] ) ) {
127 return $this->handle_oembed();
128 }
129 }
130
131 /**
132 * Handle an oembed request
133 */
134 public function handle_oembed() {
135 // Check this request is valid
136 if ( $_GET['github_oembed'] !== $this->get_key() ) {
137 header( 'HTTP/1.0 403 Forbidden' );
138 die( 'Sad Octocat is sad.' );
139 }
140
141 // Check we have the required information
142 $url = isset( $_REQUEST['url'] ) ? $_REQUEST['url'] : null;
143 $format = isset( $_REQUEST['format'] ) ? $_REQUEST['format'] : null;
144
145 if ( ! empty( $format ) && 'json' !== $format ) {
146 header( 'HTTP/1.0 501 Not implemented' );
147 die( 'This octocat only does json' );
148 }
149
150 if ( ! $url ) {
151 header( 'HTTP/1.0 404 Not Found' );
152 die( 'Octocat is lost, and afraid' );
153 }
154
155 // Issues / Milestones
156 if ( preg_match( '#https?://github.com/([^/]*)/([^/]*)/graphs/contributors/?$#i', $url, $matches ) && ! empty( $matches[2] ) ) {
157 $this->oembed_github_repo_contributors( $matches[1], $matches[2] );
158 } elseif ( preg_match( '#https?://github.com/([^/]*)/([^/]*)/issues.*$#i', $url, $matches ) && ! empty( $matches[2] ) ) {
159 if ( preg_match( '#issues.?milestone=([0-9]*)#i', $url, $milestones ) ) {
160 $milestone = $milestones[1];
161 } else {
162 $milestone = null;
163 }
164 if ( $milestone ) {
165 $this->oembed_github_repo_milestone_summary( $matches[1], $matches[2], $milestone );
166 }
167 } elseif ( preg_match( '#https?://github.com/([^/]*)/([^/]*)/milestone/([0-9]*)$#i', $url, $matches ) ) {
168 // New style milestone URL, e.g. https://github.com/example/example/milestone/1.
169 $this->oembed_github_repo_milestone_summary( $matches[1], $matches[2], $matches[3] );
170 } elseif ( preg_match( '#https?://github.com/([^/]*)/([^/]*)/?$#i', $url, $matches ) && ! empty( $matches[2] ) ) {
171 // Repository.
172 $this->oembed_github_repo( $matches[1], $matches[2] );
173 } elseif ( preg_match( '#https?://github.com/([^/]*)/?$#i', $url, $matches ) ) {
174 // User.
175 $this->oembed_github_author( $matches[1] );
176 }
177 }
178
179 /**
180 * Retrieve a list of contributors for a project
181 * @param string $owner The owner of the repository
182 * @param string $repository The repository name
183 */
184 private function oembed_github_repo_contributors( $owner, $repository ) {
185 $repo = $this->api->get_repo( $owner, $repository );
186 $contributors = $this->api->get_repo_contributors( $owner, $repository );
187
188 $response = new stdClass();
189 $response->type = 'rich';
190 $response->width = '10';
191 $response->height = '10';
192 $response->version = '1.0';
193 $response->title = $repo->description;
194
195 $gravatar_size = apply_filters( 'github_oembed_gravatar_size', 64 );
196
197 // @TODO This should all be templated
198 $response->html = '<div class="github-embed github-embed-repo-contributors">';
199 $response->html .= '<p><a href="' . esc_attr( $repo->html_url ) . '" target="_blank">';
200 $response->html .= '<strong>' . esc_html( $repo->description ) . '</strong></a><br/>';
201 $response->html .= '<span class="github-heading">Contributors: </span>';
202 $response->html .= '<ul class="github-repo-contributors">';
203 foreach ( $contributors as $contributor ) {
204 $response->html .= '<li class="github-repo-contributor">';
205 $response->html .= '<img class="github-repo-contributor-avatar" src="';
206 $response->html .= esc_url( add_query_arg( array( 's' => $gravatar_size ), $contributor->author->avatar_url ) );
207 $response->html .= '" alt="Picture of ' . esc_attr( $contributor->author->login ) . '">';
208 $response->html .= '<span class="github-repo-contributor-login">';
209 $response->html .= '<a href="https://github.com/' . esc_attr( $contributor->author->login ) . '">' . esc_attr( $contributor->author->login ) . '</a></span>';
210 }
211 $response->html .= '</ul>';
212 $response->html .= '<div style="clear: both;"></div>';
213 $response->html .= '</div>';
214 header( 'Content-Type: application/json' );
215 echo json_encode( $response );
216 die();
217 }
218
219 /**
220 * Retrieve the summary information for a repo's milestone, and
221 * output it as an oembed response
222 */
223 private function oembed_github_repo_milestone_summary( $owner, $repository, $milestone ) {
224 $repo = $this->api->get_repo( $owner, $repository );
225 $summary = $this->api->get_repo_milestone_summary( $owner, $repository, $milestone );
226
227 $response = new stdClass();
228 $response->type = 'rich';
229 $response->width = '10';
230 $response->height = '10';
231 $response->version = '1.0';
232 $response->title = $repo->description;
233
234 // @TODO This should all be templated
235 $response->html = '<div class="github-embed github-embed-milestone-summary">';
236 $response->html .= '<p><a href="' . esc_attr( $repo->html_url ) . '" target="_blank"><strong>' . esc_html( $repo->description ) . '</strong></a><br/>';
237
238 $response->html .= '<span class="github-heading">Milestone: </span>';
239 $response->html .= '<span class="github-milestone-title">' . esc_html( $summary->title ) . '</span><br>';
240
241 $response->html .= '<span class="github-heading">Issues: </span>';
242 $response->html .= '<span class="github-milestone-issues">';
243 $response->html .= esc_html( number_format_i18n( $summary->open_issues ) ) . ' open, ';
244 $response->html .= esc_html( number_format_i18n( $summary->closed_issues ) ) . ' closed.</span><br>';
245
246 if ( ! empty( $summary->due_on ) ) {
247 $response->html .= '<span class="github-heading">Due: </span>';
248 $due_date = date_format( date_create( $summary->due_on ), 'jS F Y' );
249 $response->html .= '<span class="github-milestone-due-date">' . esc_html( $due_date ) . '</span><br>';
250 }
251
252 $response->html .= '<p class="github-milestone-description">' . nl2br( esc_html( $summary->description ) ) . '</p><br>';
253 $response->html .= '</div>';
254
255 header( 'Content-Type: application/json' );
256 echo json_encode( $response );
257 die();
258
259 }
260
261 /**
262 * Retrieve the information from github for a repo, and
263 * output it as an oembed response
264 */
265 private function oembed_github_repo ( $owner, $repository ) {
266 $repo = $this->api->get_repo( $owner, $repository );
267 $commits =$this->api->get_repo_commits( $owner, $repository );
268
269 $response = new stdClass();
270 $response->type = 'rich';
271 $response->width = '10';
272 $response->height = '10';
273 $response->version = '1.0';
274 $response->title = $repo->description;
275
276 // @TODO This should all be templated
277 $response->html = '<div class="github-embed github-embed-repository">';
278 $response->html .= '<p><a href="' . esc_attr( $repo->html_url ) . '" target="_blank"><strong>' . esc_html( $repo->description ) . '</strong></a><br/>';
279 $response->html .= '<a href="' . esc_attr( $repo->html_url ) . '" target="_blank">' . esc_html( $repo->html_url ) . '</a><br/>';
280 $response->html .= '<a href="' . esc_attr( $repo->html_url . '/network' ) . '" target="_blank">' . esc_html( number_format_i18n( $repo->forks_count ) ) . '</a> forks.<br/>';
281 $response->html .= '<a href="' . esc_attr( $repo->html_url . '/stargazers' ) . '" target="_blank">' . esc_html( number_format_i18n( $repo->stargazers_count ) ) . '</a> stars.<br/>';
282 $response->html .= '<a href="' . esc_attr( $repo->html_url . '/issues' ) . '" target="_blank">' . esc_html( number_format_i18n( $repo->open_issues_count ) ) . '</a> open issues.<br/>';
283
284 if ( count( $commits ) ) {
285 $cnt = 0;
286 $response->html .= 'Recent commits:';
287 $response->html .= '<ul class="github_commits">';
288 foreach ( $commits as $commit ) {
289 if ( $cnt > 4 ) {
290 break;
291 }
292 $response->html .= '<li class="github_commit">';
293 $response->html .= '<a href="https://github.com/' . $owner . '/' . $repository . '/commit/' . esc_attr( $commit->sha ) . '" target="_blank">' . esc_html( $commit->commit->message ) . '</a>, ';
294 $response->html .= esc_html( $commit->commit->committer->name );
295 $response->html .= '</li>';
296 $cnt++;
297 }
298 $response->html .= '</ul>';
299 }
300 $response->html .= '</p>';
301 $response->html .= '</div>';
302 header( 'Content-Type: application/json' );
303 echo json_encode( $response );
304 die();
305 }
306
307 /**
308 * Retrieve the information from github for an author, and output
309 * it as an oembed response
310 */
311 private function oembed_github_author ( $owner ) {
312
313 $owner_info = $this->api->get_user( $owner );
314
315 $response = new stdClass();
316 $response->type = 'rich';
317 $response->width = '10';
318 $response->height = '10';
319 $response->version = '1.0';
320 $response->title = $owner_info->name;
321
322 // @TODO This should all be templated
323 $response->html = '<div class="github-embed github-embed-user">';
324 $response->html .= '<p><a href="https://github.com/' . esc_attr( $owner ) . '" target="_blank"><strong>' . esc_html( $owner ) . '</strong></a><br/>';
325 $response->html .= esc_html( number_format_i18n( $owner_info->public_repos ) ) . ' repositories, ';
326 $response->html .= esc_html( number_format_i18n( $owner_info->followers ) ) . ' followers.</p>';
327 $response->html .= '</div>';
328 header( 'Content-Type: application/json' );
329 echo json_encode( $response );
330 die();
331 }
332 }
333
334 require_once( 'github-api.php' );
335
336 $github_api = new github_api();
337 $github_embed = new github_embed( $github_api );
338