WordPress Page Template für einen Google+ Feed

Google+ ist im Kommen, auch gerade wegen den neuen Unternehmensseiten, welche unter anderem als Blog genutzt werden können. Doch einen RSS Feed sucht man vergeblich.

Unternehmensseite: Google+ Your Business

Für mein Projekt WordPress Trac Activity wolte ich für die nicht Google+ Nutzer einen Feed anbieten. Google selbst bietet aber keinen Feed für den Google+ Stream an.

Aus diesem Grund habe ich ein Page Template für WordPress entwickelt, welches die Möglichkeit bietet, einen Feed für einen Google+ Stream zu erstellen.

Das Page Template

Folgend der etwas größere Schnipsel mit anschließender Erklärung.

PHP / RAW / github:gist
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
<?php
/**
 * Template Name: Google+ Feed
 */


/**
 * A WordPress page template for a Google+ feed.
 *
 * @author Dominik Schilling
 * @license GPLv2
 * @link https://dominikschilling.de/567/
 *
 * @version 0.2
 */
class Google_Plus_Feed {
	/**
	 * Google+ API Key.
	 * See https://code.google.com/apis/console/
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	private $api_key = '';

	/**
	 * Google+ User ID.
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	private $user_id = '';
	
	/**
	 * Custom settings.
	 * See $this->set_settings()
	 *
	 * @since 0.1
	 *
	 * @var array
	 */
	private $settings = array();
	
	/**
	 * Don't change the lines after these
	 * if you don't know what you do.
	 */

	/**
	 * Google+ activities.
	 *
	 * @since 0.1
	 *
	 * @var mixed
	 */
	private $activities;

	/**
	 * Current queried object.
	 *
	 * @since 0.1
	 *
	 * @var object
	 */
	private $curr_obj;

	/**
	 * Construct.
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	function __construct() {
		if( empty( $this->curr_obj ) )
			$this->curr_obj = get_queried_object();

		if( ! $this->get_api_key() || ! $this->get_user_id() )
			wp_die( 'Not configured!' );

		$this->set_settings();

		$this->activities = $this->build_activities();

		if ( empty( $this->activities ) )
			wp_die( 'Please load again.' );

		$this->feed_output();
	}

	/**
	 * Get the API key from
	 *    post custom field (gplusfeed_api_key)
	 * or
	 *    $this->api_key
	 *
	 * @since 0.1
	 *
	 * @return string|bool Key on success, false if key is missing.
	 */
	function get_api_key() {
		$api_key = get_post_meta( $this->curr_obj->ID, 'gplusfeed_api_key', true );

		if ( ! empty( $api_key ) )
			return $api_key;
		else if( ! empty( $this->api_key ) )
			return $this->api_key;
		else
			return false;
	}

	/**
	 * Get the user id from
	 *    post custom field (gplus_feed_user_id)
	 * or
	 *    $this->user_id
	 *
	 * @since 0.1
	 *
	 * @return string|bool Key on success, false if key is missing.
	 */
	function get_user_id() {
		$user_id = get_post_meta( $this->curr_obj->ID, 'gplus_feed_user_id', true );

		if ( ! empty( $user_id ) )
			return $user_id;
		else if( ! empty( $this->user_id ) )
			return $this->user_id;
		else
			return false;
	}

	/**
	 * Set custom feed settings.
	 *
	 *  - limit: How many activities should be displayed in feed
	 *  - cache_time: How long the feed should be cached
	 *  - language: The language of the feed
	 *  - update_period: See http://web.resource.org/rss/1.0/modules/syndication/
	 *  - update_frequency: See http://web.resource.org/rss/1.0/modules/syndication/
	 *
	 * @since 0.1
	 *
	 * @return array Settings saved in an array.
	 */
	function set_settings() {
		$this->settings = wp_parse_args(
			array(
				'limit' => 10,
				'cache_time' => 300,
				'language' => 'de',
				'update_period' => 'hourly',
				'update_frequency' => 1
			),
			$this->settings
		);
	}

	/**
	 * Get the Google+ API link.
	 * See https://developers.google.com/+/api/latest/activities/list
	 *
	 * @since 0.1
	 *
	 * @return string URL of the Google+ API.
	 */
	function get_api_link() {
		$api_link = add_query_arg(
			array(
				'alt' => 'json',
				'maxResults' => $this->settings['limit'],
				'pp' => 1,
				'key' => $this->get_api_key()
			),
			sprintf( 'https://www.googleapis.com/plus/v1/people/%s/activities/public', $this->get_user_id() )
		);

		return esc_url_raw( $api_link );
	}

	/**
	 * Build the activity stream and save it in a transient.
	 *
	 * @since 0.1
	 *
	 * @return string|bool Activities in XML format or false on error.
	 */
	function build_activities() {
		$dev = true;
		if ( false !== ( $activities = get_transient( $this->get_cache_key() ) ) && ! $dev )
			return $activities;

		$activities = $this->get_activities();

		if ( is_wp_error( $activities ) )
			return false;

		$activities = $this->render_activities( $activities );

		if ( empty( $activities ) )
			return false;

		// Cache activities
		set_transient( $this->get_cache_key(), $activities, $this->settings['cache_time'] );

		return $activities;
	}

	/**
	 * Build an individual cache key.
	 * Based on settings, user id and API key.
	 *
	 * @since 0.1
	 *
	 * @return string The cache key.
	 */
	function get_cache_key() {
		return 'gplus_feed_' . md5(
			implode( '|', $this->settings )
			. $this->get_user_id()
			. $this->get_api_key()
		);
	}

	/**
	 * Get the activities from Google+ API.
	 *
	 * @since 0.1
	 *
	 * @return array|WP_Error API result in an array or WP_Error on API/HTTP error.
	 */
	function get_activities() {
		$result = wp_remote_retrieve_body(
			wp_remote_get(
				$this->get_api_link()
			)
		);

		if ( ! empty( $result ) ) {
			$result = (array) json_decode( $result );

			if ( empty ( $results->error ) )
				return $result;
			else
				return new WP_Error( 'api_error', 'API ERROR', $results->error );
		} else {
			return new WP_Error( 'http_error', 'HTTP ERROR');
		}
	}

	/**
	 * Render the activities and return them in XML format.
	 *
	 * @since 0.1
	 *
	 * @param array $activities
	 * @return array Rendered activity items for feed.
	 */
	function render_activities( $activities ) {
		$feed = array();

		foreach( $activities['items'] as $item => $data ) {
			if ( ! preg_match('/^<bb[^>]*>(.*?)</b>/i', $data->object->content, $matches ) ) {
				$title = $data->title;
				$content = $data->object->content;
			} else {
				$title = strip_tags( $matches[1] );
				$content = str_replace( $matches[0] . '<br /><br />', '', $data->object->content );
			}

			if ( ! empty( $data->object->attachments ) )
				$content .= $this->_render_activity_attachments( $data->object->attachments );

			$feed[$item] = "
<item>
	<title>{$title}</title>
	<link>{$data->url}</link>
	<pubDate>" . mysql2date( 'D, d M Y H:i:s +0000', $data->published, false ) . "</pubDate>
	<dc:creator>{$data->actor->displayName}</dc:creator>
	<guid isPermaLink="false">{$data->url}</guid>

	<description><![CDATA[" . wp_trim_words( $content, 20 ) . "]]></description>
	<content:encoded><![CDATA[{$content}]]></content:encoded>

	<slash:comments>{$data->object->replies->totalItems}</slash:comments>
</item>
";
		}

		return $feed;
	}

	/**
	 * Handle activity attachments, like video, photo and links (articles).
	 *
	 * @since 0.2
	 *
	 * @param array $attachments
	 * @return string Attachments in HTML format.
	 */
	function _render_activity_attachments( $attachments ) {
		$articles = $photos = $videos = array();

		foreach ( $attachments as $attachment => $meta ) {
			switch ( $meta->objectType ) {
				case 'article' :
					$host = @parse_url( $meta->url, PHP_URL_HOST );
					$articles[] = sprintf(
						'%s<a href="%s" title="%s">%s</a>',
						empty( $host ) ? '' : $host . ' – ',
						esc_url( $meta->url ),
						empty( $meta->content ) ? '' : esc_attr( $meta->content ),
						empty( $meta->displayName ) ? '' : $meta->displayName
					);
					break;
				case 'photo' :
					$photos[] = sprintf(
						'%s<img src="%s" height="%s" width="%s" alt="%s" title="%s" style="margin:5px;max-width:250px;height:auto" />%s',
						empty( $meta->fullImage->url ) ? '' : '<a href="' . esc_url( $meta->fullImage->url ) . '">',
						esc_url( $meta->image->url ),
						empty( $meta->image->height ) ? '' : esc_attr( $meta->image->height ),
						empty( $meta->image->width ) ? '' : esc_attr( $meta->image->width ),
						empty( $meta->displayName ) ? '' : $meta->displayName,
						empty( $meta->displayName ) ? '' : $meta->displayName,
						empty( $meta->fullImage->url ) ? '' : '</a>'
					);
					break;
				case 'video' :
					$videos[] = sprintf(
						'<a href="%s"><img src="%s" height="%s" width="%s" alt="%s" title="%s" style="margin:5px;max-width:250px;height:auto" /></a>',
						esc_url( $meta->url ),
						esc_url( $meta->image->url ),
						empty( $meta->image->height ) ? '' : esc_attr( $meta->image->height ),
						empty( $meta->image->width ) ? '' : esc_attr( $meta->image->width ),
						empty( $meta->displayName ) ? '' : $meta->displayName,
						empty( $meta->displayName ) ? '' : $meta->displayName
					);
					break;
			}
		}
		
		$content = '';
		$_html = '<div style="display:table-row"><div style="display:table-cell;vertical-align:top;padding:0 10px 0 0"><b>%s:</b></div>';
		if ( ! empty( $articles ) ) {
			$articles = implode( '<br />', $articles );
			$content .= sprintf(
				$_html,
				__( 'Links' )
			);
			$content .= sprintf(
				'<div style="display:table-cell">%s</div></div>',
				$articles
			);
		}
		
		if ( ! empty( $photos ) ) {
			$photos = implode( '', $photos );
			$content .= sprintf(
				$_html,
				__( 'Images' )
			);
			$content .= sprintf(
				'<div style="display:table-cell">%s</div></div>',
				$photos
			);
		}
		
		if ( ! empty( $videos ) ) {
			$videos = implode( '', $videos );
			$content .= sprintf(
				$_html,
				__( 'Video' )
			);
			$content .= sprintf(
				'<div style="display:table-cell">%s</div></div>',
				$videos
			);
		}
		
		if ( ! empty( $content ) )
			$content = sprintf(
				'<br /><br /><h4>%s</h4><div style="display:table">%s</div>',
				__( 'Images and Attachments' ),
				$content
			);

		return $content;
	}

	/**
	 * Build the feed output.
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	function feed_output() {
		$this->get_rss_header();
		$this->get_rss_content();
		$this->get_rss_footer();
	}

	/**
	 * Retrieve information about the feed.
	 *
	 * @since 0.1
	 *
	 * @param string $show Feed info to retrieve.
	 * @param bool $echo Return or print the info. Default: true.
	 * @return string Feed info.
	 */
	function get_feed_info( $show, $echo = true ) {
		switch ( $show ) {
			case 'title' :
				$output = apply_filters( 'the_title_rss', $this->curr_obj->post_title ) ;
				break;
			case 'self' :
				$host = @parse_url( home_url() );
				$host = $host['host'];
				$output = esc_url(
					( is_ssl() ? 'https' : 'http' ) . '://'
					. $host
					. stripslashes( $_SERVER['REQUEST_URI'] )
				);
				break;
			case 'link' :
				$output = esc_url( sprintf( 'https://plus.google.com/%d/', $this->get_user_id() ) );
				break;
			case 'desc' :
				$output = apply_filters( 'the_title_rss', $this->curr_obj->post_content );
				break;
			case 'time' :
				$timeout = get_option( '_transient_timeout_' . $this->get_cache_key() );
				if ( $timeout === false )
					$time = time();
				else
					$time = $timeout - $this->settings['cache_time'];
				$output = date( 'D, d M Y H:i:s +0000', $time );
				break;
			case 'language' :
				$output = $this->settings['language'];
				break;
			case 'update_period' :
				$output = $this->settings['update_period'];
				break;
			case 'update_frequency' :
				$output = $this->settings['update_frequency'];
				break;
		}

		if( ! $echo )
			return $output;

		echo $output;
	}

	/**
	 * Print the feed header.
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	function get_rss_header() {
		header( 'Content-Type: text/xml; charset=UTF-8', true );
		echo '<?xml version="1.0" encoding="UTF-8"?'.'>';
	?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
	<title><?php $this->get_feed_info( 'title' ); ?></title>
	<atom:link href="<?php $this->get_feed_info( 'self' ); ?>" rel="self" type="application/rss+xml" />
	<link><?php $this->get_feed_info( 'link' ); ?></link>
	<description><?php $this->get_feed_info( 'desc' ); ?></description>
	<lastBuildDate><?php $this->get_feed_info( 'time' ); ?></lastBuildDate>
	<language><?php $this->get_feed_info( 'language' ); ?></language>
	<sy:updatePeriod><?php $this->get_feed_info( 'update_period' ); ?></sy:updatePeriod>
	<sy:updateFrequency><?php $this->get_feed_info( 'update_frequency' ); ?></sy:updateFrequency>
	<?php
	}

	/**
	 * Print the feed content/activities.
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	function get_rss_content() {
		foreach( $this->activities as $item )
			echo $item;
	}

	/**
	 * Print the feed footer.
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	function get_rss_footer() {
	?>
</channel>
</rss>
	<?php
	}
}

// Init
$feed = new Google_Plus_Feed();

Das obige Page Template sollte mit jedem Theme kompatibel sein und muss ins das Themeverzeichnis des aktuellen Themes kopiert werden.
Bei Twenty Eleven wäre das zum Beispiel /wp-content/themes/twentyeleven/google-plus-feed.php.

Für die Einrichtung des Feeds werden ein Google+ API Key sowie die Google+ User ID benötigt:

Google+ API Key

Wie jede API von Google benötigt auch die Google+ API einen API Key, welcher vorher generiert werden muss.

Google+ API freischalten

Generiert kann sich dieser in der Google APIs Console unter code.google.com/apis/console/. Unter dem Tab Services muss dazu der zugehörige Regler auf On gesetzt werden.

Google+ User ID

Die User ID des Google+ Profils kann aus der jeweiligen URL zum Profil herausgefiltert werden, ein Beispiel:

https://plus.google.com/u/0/101675293278434581718/posts

101675293278434581718 wären in diesem Fall die User ID.

Einrichtung

Wenn API Key und User ID vorhanden sind, kann der Feed eingerichtet werden.
Dazu ins Backend wechseln und eine neue Seite einrichten, Titel und Inhalt (= Beschreibung des Feeds, ohne HTML Tags) festlegen und in der Metabox Attribute das Template Google+ Feed definieren.
Anschließend müssen der Key und die ID übergeben werden, dazu gibt es zwei Möglichkeiten:

  1. Die heruntergeladene Datei google-plus-feed.php öffnen und die Variablen private $user_id = ''; und private $api_key = '' mit den jeweiligen Werten belegen.
  2. Die zweite Möglichkeit geht über die benutzerdefinierten Feldern (ggf. über Reiter Optionen einblenden aktivieren). Dafür müssen zwei neue Felder angelegt werden, einmal gplus_feed_user_id mit dem Wert der User ID und einmal gplusfeed_api_key mit dem Wert des API Keys.

Speichern nicht vergessen und die Grundeinrichtung ist getan. Der Feed sollte jetzt erreichbar sein.

Zusätzliche Einstellungen

Weitere Anpassung an den RSS Feed können durch die Variable $this->settings getätigt werden. Dazu sollte sich die Methode set_settings() zu Rate gezogen werden.

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<?php

<?php
/**
 * Set custom feed settings.
 *
 *  - limit: How many activities should be displayed in feed
 *  - cache_time: How long the feed should be cached
 *  - language: The language of the feed
 *  - update_period: See http://web.resource.org/rss/1.0/modules/syndication/
 *  - update_frequency: See http://web.resource.org/rss/1.0/modules/syndication/
 *
 * @since 0.1
 *
 * @return array Settings saved in an array.
 */
function set_settings() {
	$this->settings = wp_parse_args(
		array(
			'limit' => 10,
			'cache_time' => 300,
			'language' => 'de',
			'update_period' => 'hourly',
			'update_frequency' => 1
		),
		$this->settings
	);
}

Formatierung

Die Google+ API gibt als Titel nur die ersten paar Wörter aus.
Dies kann aber umgangen werden, wenn der Google+ Post mit einer in Fett formatierten Zeile gefolgt von zwei Absätzen beginnt, ein Bespiel:

Google+ Formatierung für die Feeddarstellung

Feedback

Feedback, Verbesserungsvorschläge, Probleme oder sonstige Fragen können gerne als Kommentar abgegeben werden und werden dort natürlich von mir beantwortet.

16 thoughts on “WordPress Page Template für einen Google+ Feed”

  1. Ich habe es nach Anleitung gemacht. In meinem Fall musste ich noch die Rechte setzen. Nun bekomme ich aber folgenden Fehler:

    Fatal error: Call to undefined function wp_trim_words() in /blabla/blablabla/webs/prod/homepage.de/www/wp-content/themes/magazine_ORDNER/google-plus-feed.php on line 284

    Da ich mich nicht so gut damit auskenne, frage ich mich, ob mir irgendwer weiterhelfen kann? Wäre super!

    1. Hallo Patrick,
      die Funktion wp_trim_words() kam erst mit WordPress 3.3.
      Für ältere Versionen kannst du die Zeile mit wp_trim_excerpt( $content ) ersetzen.

  2. Wahnsinn! Ein Riesen-Dankeschön für die fixe Antwort! Ja, habe bisher noch kein Update gemacht. Ich werde es gleich mal ausprobieren mit dem Ersetzen.

  3. Habe leider einen andere Fehler nun:
    Hier kann man es selber anschauen:

    weblog-deluxe.de/info/google-updates/

    Scheint mir auch irgendwie als wenn ein Beitrag von G+ doppelt angezeigt werden möchte?

  4. wp_trim_excerpt( wp_strip_all_tags( $content ) ) habe nun eingefügt, Fehler hat sich “verringert”. Die Darstellung allerdings ist leider immer noch nicht iO.

    1. Das heißt, dass irgendwo schon etwas ausgeben wurde ist, meistens ein Leerzeichen. Schau mal, ob das < ?php direkt am Anfang der Datei ist.

  5. Vielen Dank für den Code, unsere Google+ Seite muss auch endlich mal “gefeeded” werden. Habe schon länger nach einer Lösung gesucht – bleibt nur noch die Frage, ob Google+ sich etabliert auf die Dauer.

Leave a Reply