diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..79207a4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab +indent_size = 4 + +[{.jshintrc,*.json,*.yml}] +indent_style = space +indent_size = 2 + +[{*.txt,wp-config-sample.php}] +end_of_line = crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6fe5be --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/node_modules/ +/vendor/ +package-lock.json +composer.lock diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..9807393 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,66 @@ +module.exports = function(grunt) { + // Project configuration. + grunt.initConfig({ + checktextdomain: { + options:{ + text_domain: 'activitypub', + keywords: [ + '__:1,2d', + '_e:1,2d', + '_x:1,2c,3d', + 'esc_html__:1,2d', + 'esc_html_e:1,2d', + 'esc_html_x:1,2c,3d', + 'esc_attr__:1,2d', + 'esc_attr_e:1,2d', + 'esc_attr_x:1,2c,3d', + '_ex:1,2c,3d', + '_n:1,2,4d', + '_nx:1,2,4c,5d', + '_n_noop:1,2,3d', + '_nx_noop:1,2,3c,4d' + ] + }, + files: { + src: [ + '**/*.php', // Include all files + 'includes/*.php', // Include includes + '!sass/**', // Exclude sass/ + '!node_modules/**', // Exclude node_modules/ + '!tests/**', // Exclude tests/ + '!vendor/**', // Exclude vendor/ + '!build/**', // Exclude build/ + '!static/**', // Exclude static resources + ], + expand: true + } + }, + + wp_readme_to_markdown: { + target: { + files: { + 'README.md': 'readme.txt' + }, + }, + }, + makepot: { + target: { + options: { + mainFile: 'activitypub.php', + domainPath: '/languages', + exclude: ['bin/.*', '.git/.*', 'vendor/.*'], + potFilename: 'activitypub.pot', + type: 'wp-plugin', + updateTimestamp: true + } + } + } + }); + + grunt.loadNpmTasks('grunt-wp-readme-to-markdown'); + grunt.loadNpmTasks('grunt-wp-i18n'); + grunt.loadNpmTasks('grunt-checktextdomain'); + + // Default task(s). + grunt.registerTask('default', ['wp_readme_to_markdown', 'makepot', 'checktextdomain']); +}; diff --git a/README.md b/README.md index c28ffba..385e5d0 100644 --- a/README.md +++ b/README.md @@ -1 +1,12 @@ -# wordpress-activitypub \ No newline at end of file +# ActivityPub # +**Contributors:** [pfefferle](https://profiles.wordpress.org/pfefferle) +**Donate link:** https://notiz.blog/donate/ +**Tags:** OStatus, fediverse, activitypub, activitystream +**Requires at least:** 4.7 +**Tested up to:** 4.9.8 +**Stable tag:** 1.0.0 +**Requires PHP:** 5.3 +**License:** MIT +**License URI:** http://opensource.org/licenses/MIT + +The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. diff --git a/activitypub.php b/activitypub.php new file mode 100644 index 0000000..045ee39 --- /dev/null +++ b/activitypub.php @@ -0,0 +1,27 @@ +=5.3.0", + "composer/installers": "~1.0" + }, + "license": "MIT", + "authors": [ + { + "name": "Matthias Pfefferle", + "email": "pfefferle@gmail.com" + } + ], + "extra": { + "installer-name": "activitypub" + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c7d6b30 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +version: '2' +services: + db: + image: mysql:5.7 + restart: always + environment: + MYSQL_ROOT_PASSWORD: wordpress + MYSQL_DATABASE: wordpress + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + + wordpress: + depends_on: + - db + image: wordpress:latest + links: + - db + ports: + - "8076:80" + volumes: + - .:/var/www/html/wp-content/plugins/activitypub + restart: always + environment: + WORDPRESS_DB_HOST: db:3306 + WORDPRESS_DB_PASSWORD: wordpress + WORDPRESS_DEBUG: 1 diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php new file mode 100644 index 0000000..421bb7d --- /dev/null +++ b/includes/class-activitypub.php @@ -0,0 +1,73 @@ +query_vars['activitypub'] ) ) { + return $json_template; + } + + if ( ! isset( $_SERVER['HTTP_ACCEPT'] ) ) { + return $template; + } + + // interpret accept header + $pos = stripos( $_SERVER['HTTP_ACCEPT'], ';' ); + if ( $pos ) { + $accept_header = substr( $_SERVER['HTTP_ACCEPT'], 0, $pos ); + } else { + $accept_header = $_SERVER['HTTP_ACCEPT']; + } + // accept header as an array + $accept = explode( ',', trim( $accept_header ) ); + + if ( + ! in_array( 'application/activity+json', $accept, true ) && + ! in_array( 'application/ld+json', $accept, true ) + ) { + return $template; + } + + return $json_template; + } + + /** + * Add WebFinger discovery links + * + * @param array $array the jrd array + * @param string $resource the WebFinger resource + * @param WP_User $user the WordPress user + */ + public static function add_webfinger_discovery( $array, $resource, $user ) { + $array['links'][] = array( + 'rel' => 'self', + 'type' => 'aplication/activity+json', + 'href' => get_author_posts_url( $user->ID ), + ); + + return $array; + } + + /** + * Add the 'photos' query variable so WordPress + * won't mangle it. + */ + public static function add_query_vars( $vars ) { + $vars[] = 'activitypub'; + return $vars; + } + + /** + * Add our rewrite endpoint to permalinks and pages. + */ + public static function add_rewrite_endpoint() { + add_rewrite_endpoint( 'activitypub', EP_AUTHORS ); + } +} diff --git a/includes/functions.php b/includes/functions.php new file mode 100644 index 0000000..e69de29 diff --git a/languages/activitypub.pot b/languages/activitypub.pot new file mode 100644 index 0000000..6316a6b --- /dev/null +++ b/languages/activitypub.pot @@ -0,0 +1,36 @@ +# Copyright (C) 2018 Matthias Pfefferle +# This file is distributed under the MIT. +msgid "" +msgstr "" +"Project-Id-Version: ActivityPub 1.0.0\n" +"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/activitypub\n" +"POT-Creation-Date: 2018-08-18 10:35:26+00:00\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2018-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"X-Generator: grunt-wp-i18n1.0.2\n" + +#. Plugin Name of the plugin/theme +msgid "ActivityPub" +msgstr "" + +#. Plugin URI of the plugin/theme +msgid "https://github.com/pfefferle/wordpress-activitypub/" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"The ActivityPub protocol is a decentralized social networking protocol " +"based upon the ActivityStreams 2.0 data format." +msgstr "" + +#. Author of the plugin/theme +msgid "Matthias Pfefferle" +msgstr "" + +#. Author URI of the plugin/theme +msgid "https://notiz.blog/" +msgstr "" \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..628b6f8 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "activitypub", + "description": "The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format.", + "repository": { + "type": "git", + "url": "git+https://github.com/pfefferle/wordpress-activitypub.git" + }, + "author": { + "name": "Matthias Pfefferle", + "web": "https://notiz.blog" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/pfefferle/wordpress-activitypub/issues" + }, + "homepage": "https://github.com/pfefferle/wordpress-activitypub#readme", + "devDependencies": { + "grunt": "^1.0.3", + "grunt-checktextdomain": "^1.0.1", + "grunt-wp-i18n": "^1.0.2", + "grunt-wp-readme-to-markdown": "^2.0.1" + } +} diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..d2e6a0f --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,21 @@ + + + WordPress ActivityPub Standards + ./activitypub.php + ./includes/ + + */includes/*\.(inc|css|js|svg) + */vendor/* + + + + + + + + + + + + + diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..fb24cbf --- /dev/null +++ b/readme.txt @@ -0,0 +1,12 @@ +=== ActivityPub === +Contributors: pfefferle +Donate link: https://notiz.blog/donate/ +Tags: OStatus, fediverse, activitypub, activitystream +Requires at least: 4.7 +Tested up to: 4.9.8 +Stable tag: 1.0.0 +Requires PHP: 5.3 +License: MIT +License URI: http://opensource.org/licenses/MIT + +The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. diff --git a/templates/author-profile.php b/templates/author-profile.php new file mode 100644 index 0000000..c100aa0 --- /dev/null +++ b/templates/author-profile.php @@ -0,0 +1,70 @@ +{'@context'} = array( + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', +); + +$json->id = get_author_posts_url( $author_id ); +$json->type = 'Person'; +$json->name = get_the_author_meta( 'display_name', $author_id ); +$json->summary = get_the_author_meta( 'description', $author_id ); +$json->preferredUsername = get_the_author(); +$json->url = get_author_posts_url( $author_id ); +$json->icon = array( + 'type' => 'Image', + 'url' => get_avatar_url( $author_id, array( 'size' => 120 ) ), +); + +if ( has_header_image() ) { + $json->image = array( + 'type' => 'Image', + 'url' => get_header_image(), + ); +} + +if ( method_exists( 'Magic_Sig', 'get_public_key' ) ) { + $json->publicKey = array( + 'id' => get_author_posts_url( $author_id ) . '#key', + 'owner' => get_author_posts_url( $author_id ), + 'publicKeyPem' => Magic_Sig::get_public_key( $author_id ), + ); +} + +header( 'Content-Type: application/activity-json', true ); + +// filter output +$json = apply_filters( 'activitypub_profile_array', $json ); + +/* + * Action triggerd prior to the ActivityPub profile being created and sent to the client + */ +do_action( 'activitypub_profile_pre' ); + +$options = 0; +// JSON_PRETTY_PRINT added in PHP 5.4 +if ( get_query_var( 'pretty' ) ) { + $options |= JSON_PRETTY_PRINT; +} + +/* + * Options to be passed to json_encode() + * + * @param int $options The current options flags + */ +$options = apply_filters( 'activitypub_profile_options', $options ); +echo wp_json_encode( $json, $options ); + +/* + * Action triggerd after the ActivityPub profile has been created and sent to the client + */ +do_action( 'activitypub_profile_post' );