mirror of
https://github.com/discourse/wp-discourse.git
synced 2025-08-17 18:11:19 +08:00
Merging changes from Got for 0.7 release
git-svn-id: http://plugins.svn.wordpress.org/wp-discourse/trunk@1449569 b8457f37-d9ea-0310-8a92-e5e31aec5664
This commit is contained in:
parent
4d91d3004a
commit
2d8feca9ce
41 changed files with 5581 additions and 385 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1 +1,6 @@
|
|||
*.swp
|
||||
|
||||
# JetBrains IDEs
|
||||
.idea/*
|
||||
|
||||
vendor/*
|
||||
|
|
4
.jscsrc
Normal file
4
.jscsrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"preset": "wordpress",
|
||||
"fileExtensions": [ ".js" ]
|
||||
}
|
13
.mention-bot
Normal file
13
.mention-bot
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"maxReviewers": 2,
|
||||
"userBlacklist": [
|
||||
"tnorthcutt",
|
||||
"retlehs"
|
||||
],
|
||||
"userBlacklistForPR": [
|
||||
"scossar",
|
||||
"retlehs",
|
||||
"techAPJ",
|
||||
"SamSaffron"
|
||||
]
|
||||
}
|
90
.travis.yml
Normal file
90
.travis.yml
Normal file
|
@ -0,0 +1,90 @@
|
|||
# Travis CI (MIT License) configuration file for the WP Discourse WordPress plugin.
|
||||
# @link https://travis-ci.org/
|
||||
|
||||
# For use with the WP Discourse WordPress plugin.
|
||||
# @link https://github.com/discourse/wp-discourse
|
||||
|
||||
# Ditch sudo and use containers.
|
||||
# @link http://docs.travis-ci.com/user/migrating-from-legacy/#Why-migrate-to-container-based-infrastructure%3F
|
||||
# @link http://docs.travis-ci.com/user/workers/container-based-infrastructure/#Routing-your-build-to-container-based-infrastructure
|
||||
sudo: false
|
||||
|
||||
# Declare project language.
|
||||
# @link http://about.travis-ci.org/docs/user/languages/php/
|
||||
language: php
|
||||
|
||||
# Declare versions of PHP to use. Use one decimal max.
|
||||
# @link http://docs.travis-ci.com/user/build-configuration/
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
# aliased to a recent 5.3.x version
|
||||
- php: '5.3'
|
||||
# aliased to a recent 5.6.x version
|
||||
- php: '5.6'
|
||||
env: SNIFF=1
|
||||
# aliased to a recent 7.x version
|
||||
- php: '7.0'
|
||||
# aliased to a recent hhvm version
|
||||
- php: 'hhvm'
|
||||
|
||||
allow_failures:
|
||||
- php: 'hhvm'
|
||||
|
||||
# Use this to prepare the system to install prerequisites or dependencies.
|
||||
# e.g. sudo apt-get update.
|
||||
# Failures in this section will result in build status 'errored'.
|
||||
# before_install:
|
||||
|
||||
# Use this to prepare your build for testing.
|
||||
# e.g. copy database configurations, environment variables, etc.
|
||||
# Failures in this section will result in build status 'errored'.
|
||||
before_script:
|
||||
- export PHPCS_DIR=/tmp/phpcs
|
||||
- export SNIFFS_DIR=/tmp/sniffs
|
||||
# Install CodeSniffer for WordPress Coding Standards checks.
|
||||
- if [[ "$SNIFF" == "1" ]]; then git clone -b master --depth 1 https://github.com/squizlabs/PHP_CodeSniffer.git $PHPCS_DIR; fi
|
||||
# Install WordPress Coding Standards.
|
||||
- if [[ "$SNIFF" == "1" ]]; then git clone -b master --depth 1 https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards.git $SNIFFS_DIR; fi
|
||||
# Install PHP Compatibility sniffs.
|
||||
- if [[ "$SNIFF" == "1" ]]; then git clone -b master --depth 1 https://github.com/wimg/PHPCompatibility.git $SNIFFS_DIR/PHPCompatibility; fi
|
||||
# Set install path for PHPCS sniffs.
|
||||
# @link https://github.com/squizlabs/PHP_CodeSniffer/blob/4237c2fc98cc838730b76ee9cee316f99286a2a7/CodeSniffer.php#L1941
|
||||
- if [[ "$SNIFF" == "1" ]]; then $PHPCS_DIR/scripts/phpcs --config-set installed_paths $SNIFFS_DIR; fi
|
||||
# After CodeSniffer install you should refresh your path.
|
||||
- if [[ "$SNIFF" == "1" ]]; then phpenv rehash; fi
|
||||
# Install JSCS: JavaScript Code Style checker.
|
||||
# @link http://jscs.info/
|
||||
- if [[ "$SNIFF" == "1" ]]; then npm install -g jscs; fi
|
||||
# Install JSHint, a JavaScript Code Quality Tool.
|
||||
# @link http://jshint.com/docs/
|
||||
- if [[ "$SNIFF" == "1" ]]; then npm install -g jshint; fi
|
||||
# Pull in the WP Core jshint rules.
|
||||
- if [[ "$SNIFF" == "1" ]]; then wget https://develop.svn.wordpress.org/trunk/.jshintrc; fi
|
||||
|
||||
# Run test script commands.
|
||||
# Default is specific to project language.
|
||||
# All commands must exit with code 0 on success. Anything else is considered failure.
|
||||
script:
|
||||
# Search for PHP syntax errors.
|
||||
- find -L . -name '*.php' -and ! -name 'test*.php' -print0 | xargs -0 -n 1 -P 4 php -l
|
||||
# Run the theme through JSHint.
|
||||
- if [[ "$SNIFF" == "1" ]]; then jshint .; fi
|
||||
# Run the theme through JavaScript Code Style checker.
|
||||
- if [[ "$SNIFF" == "1" ]]; then jscs .; fi
|
||||
# WordPress Coding Standards.
|
||||
# @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards
|
||||
# @link http://pear.php.net/package/PHP_CodeSniffer/
|
||||
# -p flag: Show progress of the run.
|
||||
# -s flag: Show sniff codes in all reports.
|
||||
# -v flag: Print verbose output.
|
||||
# -n flag: Do not print warnings. (shortcut for --warning-severity=0)
|
||||
# --standard: Use WordPress as the standard.
|
||||
# --extensions: Only sniff PHP files.
|
||||
- if [[ "$SNIFF" == "1" ]]; then $PHPCS_DIR/scripts/phpcs -p -s -v -n . --standard=./codesniffer.ruleset.xml --ignore=*/tests/*,*/tests/lib/* --extensions=php; fi
|
||||
|
||||
# Receive notifications for build results.
|
||||
# @link http://docs.travis-ci.com/user/notifications/#Email-notifications
|
||||
notifications:
|
||||
email: false
|
45
CHANGELOG.md
Normal file
45
CHANGELOG.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
### 0.7.0: May 18th, 2016
|
||||
* Move templates out of options ([#194](https://github.com/discourse/wp-discourse/pull/194))
|
||||
* Validate settings ([#189](https://github.com/discourse/wp-discourse/pull/189))
|
||||
* Add notices to indicate connection status ([#193](https://github.com/discourse/wp-discourse/pull/193))
|
||||
* Sanitize admin options page ([#196](https://github.com/discourse/wp-discourse/pull/196))
|
||||
* Sanitize comment template output ([#195](https://github.com/discourse/wp-discourse/pull/195))
|
||||
* Add type argument to text input method ([#192](https://github.com/discourse/wp-discourse/pull/192))
|
||||
* Use cached categories when there is a configuration error ([#191](https://github.com/discourse/wp-discourse/pull/191))
|
||||
* Fix name property not available in participants array ([#187](https://github.com/discourse/wp-discourse/pull/187))
|
||||
* Use `wp_get_current_user` ([#185](https://github.com/discourse/wp-discourse/pull/185))
|
||||
* Fix `add_query_arg` undefined offset notice ([#184](https://github.com/discourse/wp-discourse/pull/184))
|
||||
* Update Discourse post on WP post update ([#176](https://github.com/discourse/wp-discourse/pull/176))
|
||||
* Better method for including comments script and other small tweaks ([#181](https://github.com/discourse/wp-discourse/pull/181))
|
||||
* Allow choosing Discourse category per post ([#177](https://github.com/discourse/wp-discourse/pull/177))
|
||||
* Replace avatar URL function ([#172](https://github.com/discourse/wp-discourse/pull/172))
|
||||
* Fix timezone for custom timestamp ([#162](https://github.com/discourse/wp-discourse/pull/162))
|
||||
|
||||
### 0.6.6: July 30th, 2015
|
||||
* Add custom datetime format string to admin settings ([#160](https://github.com/discourse/wp-discourse/pull/160))
|
||||
* Add a log entry when HTTP request fails ([#159](https://github.com/discourse/wp-discourse/pull/159))
|
||||
* Log out of WordPress when logging out of Discourse ([#158](https://github.com/discourse/wp-discourse/pull/158))
|
||||
* Fix security issue, add missing `esc_url_raw()` ([#157](https://github.com/discourse/wp-discourse/pull/157))
|
||||
* Fix SSO login ([#156](https://github.com/discourse/wp-discourse/pull/156))
|
||||
* Use `wp_remote_get` instead of `file_get_contents` ([#155](https://github.com/discourse/wp-discourse/pull/155))
|
||||
* Fix user mention links ([8b6fe46](https://github.com/discourse/wp-discourse/commit/8b6fe46bdbeaa6f4be490723f1e9d6b5a6f48d41))
|
||||
* Allow showing existing WP comments under Discourse ([#137](https://github.com/discourse/wp-discourse/pull/137))
|
||||
* Add `<time>` to allowed tags ([#135](https://github.com/discourse/wp-discourse/pull/135))
|
||||
* Don't do a replace if already an absolute URL ([#131](https://github.com/discourse/wp-discourse/pull/131))
|
||||
|
||||
### 0.6.5: February 28th, 2015
|
||||
* Whitespaces should be stripped only on strings
|
||||
|
||||
### 0.6.4: February 24th, 2015
|
||||
* Minor re-organization of the settings page
|
||||
* Fetch categories from remote
|
||||
* Add fixes for allowed post types
|
||||
* Fix asset URLs on synced Discourse comments
|
||||
|
||||
### 0.6.3: January 31st, 2015
|
||||
* Add CHANGELOG
|
||||
* Move comments template into new folder
|
||||
* Move SSO and admin functions into separate files
|
||||
* Switch from `register_uninstall_hook` to `uninstall.php`
|
||||
* Move JS from separate file to inline
|
||||
* Remove unnecessary stylesheet
|
49
README.md
49
README.md
|
@ -1,8 +1,47 @@
|
|||
wp-discourse
|
||||
============
|
||||
# wp-discourse
|
||||
|
||||
This plugin allows you to *optionally* publish your WordPress blog posts on a Discourse instance.
|
||||
This WordPress plugin allows you to **use Discourse as a community engine for your WordPress blog**.
|
||||
|
||||
Once published, WordPress will fetch the best 5 comments from the Discourse topic and surplant the existing comments section.
|
||||
## Features
|
||||
|
||||
Comments and post metadata is synchronized every 10 minutes.
|
||||
* Optionally publish all new posts to Discourse automatically
|
||||
* Use Discourse to comment on blog posts with associated Discourse topics
|
||||
* Periodically sync the "best" posts in Discourse topics back to the associated WordPress blog entry as WordPress comments
|
||||
* Enable SSO to Discourse
|
||||
* Define format of post on Discourse
|
||||
* Set username of post on Discourse
|
||||
* Set published category on Discourse
|
||||
* Allow author to automatically track published Discourse topics
|
||||
* Show comments on Discourse based on post score and commenter trust level
|
||||
|
||||
## Installation
|
||||
|
||||
### Plugin manager from your `wp-admin`
|
||||
|
||||
Download the [latest release](https://github.com/discourse/wp-discourse/releases) and upload it in the WordPress admin from Plugins > Add New > Upload Plugin.
|
||||
|
||||
### Composer
|
||||
|
||||
If you're using Composer to manage WordPress, add WP-Discourse to your project's dependencies. Run:
|
||||
|
||||
```sh
|
||||
composer require discourse/wp-discourse 0.7.0
|
||||
```
|
||||
|
||||
Or manually add it to your `composer.json`:
|
||||
|
||||
```json
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"wordpress": "4.1.1",
|
||||
"discourse/wp-discourse": "0.7.0"
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork this repo
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create a new pull request
|
||||
|
|
BIN
assets/screenshot-1.png
Normal file
BIN
assets/screenshot-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
BIN
assets/screenshot-2.png
Normal file
BIN
assets/screenshot-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
BIN
assets/screenshot-3.png
Normal file
BIN
assets/screenshot-3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
BIN
assets/screenshot-4.png
Normal file
BIN
assets/screenshot-4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
118
bin/install-wp-tests.sh
Executable file
118
bin/install-wp-tests.sh
Executable file
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DB_NAME=$1
|
||||
DB_USER=$2
|
||||
DB_PASS=$3
|
||||
DB_HOST=${4-localhost}
|
||||
WP_VERSION=${5-latest}
|
||||
|
||||
WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
|
||||
WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}
|
||||
|
||||
download() {
|
||||
if [ `which curl` ]; then
|
||||
curl -s "$1" > "$2";
|
||||
elif [ `which wget` ]; then
|
||||
wget -nv -O "$2" "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then
|
||||
WP_TESTS_TAG="tags/$WP_VERSION"
|
||||
elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
|
||||
WP_TESTS_TAG="trunk"
|
||||
else
|
||||
# http serves a single offer, whereas https serves multiple. we only want one
|
||||
download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
|
||||
grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
|
||||
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
|
||||
if [[ -z "$LATEST_VERSION" ]]; then
|
||||
echo "Latest WordPress version could not be found"
|
||||
exit 1
|
||||
fi
|
||||
WP_TESTS_TAG="tags/$LATEST_VERSION"
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
install_wp() {
|
||||
|
||||
if [ -d $WP_CORE_DIR ]; then
|
||||
return;
|
||||
fi
|
||||
|
||||
mkdir -p $WP_CORE_DIR
|
||||
|
||||
if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
|
||||
mkdir -p /tmp/wordpress-nightly
|
||||
download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip
|
||||
unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/
|
||||
mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR
|
||||
else
|
||||
if [ $WP_VERSION == 'latest' ]; then
|
||||
local ARCHIVE_NAME='latest'
|
||||
else
|
||||
local ARCHIVE_NAME="wordpress-$WP_VERSION"
|
||||
fi
|
||||
download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz
|
||||
tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
|
||||
fi
|
||||
|
||||
download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
|
||||
}
|
||||
|
||||
install_test_suite() {
|
||||
# portable in-place argument for both GNU sed and Mac OSX sed
|
||||
if [[ $(uname -s) == 'Darwin' ]]; then
|
||||
local ioption='-i .bak'
|
||||
else
|
||||
local ioption='-i'
|
||||
fi
|
||||
|
||||
# set up testing suite if it doesn't yet exist
|
||||
if [ ! -d $WP_TESTS_DIR ]; then
|
||||
# set up testing suite
|
||||
mkdir -p $WP_TESTS_DIR
|
||||
svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
|
||||
fi
|
||||
|
||||
if [ ! -f wp-tests-config.php ]; then
|
||||
download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
install_db() {
|
||||
# parse DB_HOST for port or socket references
|
||||
local PARTS=(${DB_HOST//\:/ })
|
||||
local DB_HOSTNAME=${PARTS[0]};
|
||||
local DB_SOCK_OR_PORT=${PARTS[1]};
|
||||
local EXTRA=""
|
||||
|
||||
if ! [ -z $DB_HOSTNAME ] ; then
|
||||
if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
|
||||
EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
|
||||
elif ! [ -z $DB_SOCK_OR_PORT ] ; then
|
||||
EXTRA=" --socket=$DB_SOCK_OR_PORT"
|
||||
elif ! [ -z $DB_HOSTNAME ] ; then
|
||||
EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
|
||||
fi
|
||||
fi
|
||||
|
||||
# create database
|
||||
mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
|
||||
}
|
||||
|
||||
install_wp
|
||||
install_test_suite
|
||||
install_db
|
18
codesniffer.ruleset.xml
Normal file
18
codesniffer.ruleset.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="WP Discourse Coding Standards">
|
||||
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml -->
|
||||
<!-- See https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/blob/develop/WordPress-Core/ruleset.xml -->
|
||||
|
||||
<!-- Set a description for this ruleset. -->
|
||||
<description>A custom set of code standard rules for the WP Discourse plugin.</description>
|
||||
|
||||
<!-- Include the WordPress ruleset, with exclusions. -->
|
||||
<rule ref="WordPress">
|
||||
<exclude name="Generic.WhiteSpace.ScopeIndent.IncorrectExact" />
|
||||
<exclude name="Generic.WhiteSpace.ScopeIndent.Incorrect" />
|
||||
<exclude name="PEAR.Functions.FunctionCallSignature.Indent" />
|
||||
</rule>
|
||||
|
||||
<!-- Include sniffs for PHP cross-version compatibility. -->
|
||||
<rule ref="PHPCompatibility"/>
|
||||
</ruleset>
|
28
composer.json
Normal file
28
composer.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "discourse/wp-discourse",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "GPLv3",
|
||||
"description": "WordPress plugin that allows you to use Discourse as a community engine for your WordPress blog.",
|
||||
"homepage": "https://github.com/discourse/wp-discourse",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sam Saffron",
|
||||
"homepage": "https://github.com/SamSaffron"
|
||||
},
|
||||
{
|
||||
"name": "Robin Ward",
|
||||
"homepage": "https://github.com/eviltrout"
|
||||
}
|
||||
],
|
||||
"keywords": ["wordpress", "discourse"],
|
||||
"support": {
|
||||
"issues": "https://github.com/discourse/wp-discourse/issues"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"composer/installers": "1.0.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-mock/php-mock-phpunit": "^0.3"
|
||||
}
|
||||
}
|
1394
composer.lock
generated
Normal file
1394
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
24
css/admin-styles.css
Normal file
24
css/admin-styles.css
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* Highlights errors in settings form inputs */
|
||||
|
||||
#setting-error-discourse_url ~ form #discourse_url,
|
||||
#setting-error-api_key ~ form #discourse_api-key,
|
||||
#setting-error-publish_username ~ form #discourse_publish-username,
|
||||
#setting-error-max_comments ~ form #discourse_max-comments,
|
||||
#setting-error-min_replies ~ form #discourse_min-replies,
|
||||
#setting-error-min_score ~ form #discourse_min-score,
|
||||
#setting-error-min_trust_level ~ form #discourse_min-trust-level,
|
||||
#setting-error-bypass_trust_level ~ form #discourse_bypass-trust-level-score,
|
||||
#setting-error-excerpt_length ~ form #discourse_custom-excerpt-length,
|
||||
#setting-error-sso_secret ~ form #discourse_sso-secret,
|
||||
#setting-error-login_path ~ form #discourse_login-path {
|
||||
border-color: #dc3232;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.settings_page_discourse .discourse-allowed-types {
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
#discourse-publish-meta-box .warning {
|
||||
padding: 0 8px;
|
||||
}
|
18
js/comments.js
Normal file
18
js/comments.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* globals discourse */
|
||||
|
||||
/**
|
||||
* Fixes Discourse oneboxes and mention links for display on WordPress.
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
jQuery( document ).ready(function() {
|
||||
jQuery( '.lazyYT' ).each(function() {
|
||||
var id = jQuery( this ).data( 'youtube-id' ),
|
||||
url = 'https://www.youtube.com/watch?v=' + id;
|
||||
jQuery( this ).replaceWith( '<a href="' + url + '">' + url + '</a>' );
|
||||
});
|
||||
jQuery( 'a.mention' ).each(function() {
|
||||
jQuery( this ).attr( 'href', discourse.url + jQuery( this ).attr( 'href' ) );
|
||||
});
|
||||
});
|
655
lib/admin.php
Normal file
655
lib/admin.php
Normal file
|
@ -0,0 +1,655 @@
|
|||
<?php
|
||||
/**
|
||||
* WP-Discourse admin settings
|
||||
*
|
||||
* @link https://github.com/discourse/wp-discourse/blob/master/lib/admin.php
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\DiscourseAdmin;
|
||||
|
||||
use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
|
||||
|
||||
/**
|
||||
* Class DiscourseAdmin
|
||||
*/
|
||||
class DiscourseAdmin {
|
||||
/**
|
||||
* Gives access to the plugin options.
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed|void
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Discourse constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'discourse' );
|
||||
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_styles' ) );
|
||||
add_action( 'admin_menu', array( $this, 'discourse_admin_menu' ) );
|
||||
add_action( 'load-settings_page_discourse', array( $this, 'connection_status_notice' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the admin stylesheet.
|
||||
*/
|
||||
public function admin_styles() {
|
||||
wp_register_style( 'wp_discourse_admin', WPDISCOURSE_URL . '/css/admin-styles.css' );
|
||||
wp_enqueue_style( 'wp_discourse_admin' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings
|
||||
*/
|
||||
public function admin_init() {
|
||||
register_setting( 'discourse', 'discourse', array( $this, 'discourse_validate_options' ) );
|
||||
add_settings_section( 'discourse_wp_api', __( 'Common Settings', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'init_default_settings',
|
||||
), 'discourse' );
|
||||
|
||||
add_settings_section( 'discourse_wp_publish', __( 'Publishing Settings', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'init_default_settings',
|
||||
), 'discourse' );
|
||||
add_settings_section( 'discourse_comments', __( 'Comments Settings', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'init_comment_settings',
|
||||
), 'discourse' );
|
||||
add_settings_section( 'discourse_wp_sso', __( 'SSO Settings', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'init_default_settings',
|
||||
), 'discourse' );
|
||||
|
||||
add_settings_field( 'discourse_url', __( 'Discourse URL', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'url_input',
|
||||
), 'discourse', 'discourse_wp_api' );
|
||||
add_settings_field( 'discourse_api_key', __( 'API Key', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'api_key_input',
|
||||
), 'discourse', 'discourse_wp_api' );
|
||||
add_settings_field( 'discourse_publish_username', __( 'Publishing username', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'publish_username_input',
|
||||
), 'discourse', 'discourse_wp_api' );
|
||||
|
||||
add_settings_field( 'discourse_enable_sso', __( 'Enable SSO', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'enable_sso_checkbox',
|
||||
), 'discourse', 'discourse_wp_sso' );
|
||||
add_settings_field( 'discourse_wp_login_path', __( 'Path to your login page', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'wordpress_login_path',
|
||||
), 'discourse', 'discourse_wp_sso' );
|
||||
add_settings_field( 'discourse_sso_secret', __( 'SSO Secret Key', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'sso_secret_input',
|
||||
), 'discourse', 'discourse_wp_sso' );
|
||||
add_settings_field( 'discourse_display_subcategories', __( 'Display subcategories', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'display_subcategories',
|
||||
), 'discourse', 'discourse_wp_publish' );
|
||||
add_settings_field( 'discourse_publish_category', __( 'Published category', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'publish_category_input',
|
||||
), 'discourse', 'discourse_wp_publish' );
|
||||
add_settings_field( 'discourse_publish_category_update', __( 'Force category update', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'publish_category_input_update',
|
||||
), 'discourse', 'discourse_wp_publish' );
|
||||
add_settings_field( 'discourse_full_post_content', __( 'Use full post content', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'full_post_checkbox',
|
||||
), 'discourse', 'discourse_wp_publish' );
|
||||
|
||||
add_settings_field( 'discourse_auto_publish', __( 'Auto Publish', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'auto_publish_checkbox',
|
||||
), 'discourse', 'discourse_wp_publish' );
|
||||
add_settings_field( 'discourse_auto_track', __( 'Auto Track Published Topics', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'auto_track_checkbox',
|
||||
), 'discourse', 'discourse_wp_publish' );
|
||||
add_settings_field( 'discourse_allowed_post_types', __( 'Post Types to publish to Discourse', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'post_types_select',
|
||||
), 'discourse', 'discourse_wp_publish' );
|
||||
|
||||
add_settings_field( 'discourse_use_discourse_comments', __( 'Use Discourse Comments', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'use_discourse_comments_checkbox',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_show_existing_comments', __( 'Show Existing WP Comments', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'show_existing_comments_checkbox',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_existing_comments_heading', __( 'Existing Comments Heading', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'existing_comments_heading_input',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_max_comments', __( 'Max visible comments', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'max_comments_input',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_min_replies', __( 'Min number of replies', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'min_replies_input',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_min_score', __( 'Min score of posts', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'min_score_input',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_min_trust_level', __( 'Min trust level', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'min_trust_level_input',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_bypass_trust_level_score', __( 'Bypass trust level score', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'bypass_trust_level_input',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_custom_excerpt_length', __( 'Custom excerpt length', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'custom_excerpt_length',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_custom_datetime_format', __( 'Custom Datetime Format', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'custom_datetime_format',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
|
||||
add_settings_field( 'discourse_only_show_moderator_liked', __( 'Only import comments liked by a moderator', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'only_show_moderator_liked_checkbox',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
add_settings_field( 'discourse_debug_mode', __( 'Debug mode', 'wp-discourse' ), array(
|
||||
$this,
|
||||
'debug_mode_checkbox',
|
||||
), 'discourse', 'discourse_comments' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Discourse username to the user contact methods.
|
||||
*
|
||||
* @param array $fields Contact information fields available to users.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function extend_user_profile( $fields ) {
|
||||
$fields['discourse_username'] = 'Discourse Username';
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds content to the top of the settings section.
|
||||
*/
|
||||
function init_default_settings() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds content to the top of the comment section.
|
||||
*/
|
||||
function init_comment_settings() {
|
||||
?>
|
||||
|
||||
<p class="documentation-link">
|
||||
<em><?php esc_html_e( 'For documentation on customizing the plugin\'s html, visit ', 'wp-discourse' ); ?></em>
|
||||
<a href="https://github.com/discourse/wp-discourse/wiki/Template-Customization">https://github.com/discourse/wp-discourse/wiki/Template-Customization</a>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the Discourse-url input.
|
||||
*/
|
||||
function url_input() {
|
||||
self::text_input( 'url', __( 'e.g. http://discourse.example.com', 'wp-discourse' ), 'url' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the login-path input.
|
||||
*/
|
||||
function wordpress_login_path() {
|
||||
self::text_input( 'login-path', __( '(Optional) The path to your login page. It should start with \'/\'. Leave blank to use the default WordPress login page.', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the api-key input.
|
||||
*/
|
||||
function api_key_input() {
|
||||
$discourse_options = $this->options;
|
||||
if ( isset( $discourse_options['url'] ) && ! empty( $discourse_options['url'] ) ) {
|
||||
self::text_input( 'api-key', __( 'Found at ', 'wp-discourse' ) . '<a href="' . esc_url( $discourse_options['url'] ) . '/admin/api" target="_blank">' . esc_url( $discourse_options['url'] ) . '/admin/api</a>' );
|
||||
} else {
|
||||
self::text_input( 'api-key', __( 'Found at http://discourse.example.com/admin/api', 'wp-discourse' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the enable-sso checkbox.
|
||||
*/
|
||||
function enable_sso_checkbox() {
|
||||
self::checkbox_input( 'enable-sso', __( 'Enable SSO to Discourse', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the sso-secret input.
|
||||
*/
|
||||
function sso_secret_input() {
|
||||
self::text_input( 'sso-secret', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the publish-username input.
|
||||
*/
|
||||
function publish_username_input() {
|
||||
self::text_input( 'publish-username', __( 'Discourse username of publisher (will be overriden if Discourse Username is specified on user)', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the display-subcategories checkbox.
|
||||
*/
|
||||
function display_subcategories() {
|
||||
self::checkbox_input( 'display-subcategories', __( 'Include subcategories in the list of available categories.', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the publish-category input.
|
||||
*/
|
||||
function publish_category_input() {
|
||||
self::category_select( 'publish-category', __( 'Default category used to published in Discourse (optional)', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the publish-category-update input.
|
||||
*/
|
||||
function publish_category_input_update() {
|
||||
self::checkbox_input( 'publish-category-update', __( 'Update the discourse publish category list, (normally set to refresh every hour)', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the max-comments input.
|
||||
*/
|
||||
function max_comments_input() {
|
||||
self::text_input( 'max-comments', __( 'Maximum number of comments to display', 'wp-discourse' ), 'number' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the aoto-publish checkbox.
|
||||
*/
|
||||
function auto_publish_checkbox() {
|
||||
self::checkbox_input( 'auto-publish', __( 'Publish all new posts to Discourse', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the auto-track checkbox.
|
||||
*/
|
||||
function auto_track_checkbox() {
|
||||
self::checkbox_input( 'auto-track', __( 'Author automatically tracks published Discourse topics', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the post-types select input.
|
||||
*/
|
||||
function post_types_select() {
|
||||
self::post_type_select_input( 'allowed_post_types',
|
||||
$this->post_types_to_publish( array( 'attachment' ) ),
|
||||
__( 'Hold the <strong>control</strong> button (Windows) or the <strong>command</strong> button (Mac) to select multiple options.', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the use-discourse-comments checkbox.
|
||||
*/
|
||||
function use_discourse_comments_checkbox() {
|
||||
self::checkbox_input( 'use-discourse-comments', __( 'Use Discourse to comment on Discourse published posts', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the show-existing-comments checkbox.
|
||||
*/
|
||||
function show_existing_comments_checkbox() {
|
||||
self::checkbox_input( 'show-existing-comments', __( 'Display existing WordPress comments beneath Discourse comments', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the existing-comments-heading input.
|
||||
*/
|
||||
function existing_comments_heading_input() {
|
||||
self::text_input( 'existing-comments-heading', __( 'Heading for existing WordPress comments (e.g. "Historical Comment Archive")', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the min-replies input.
|
||||
*/
|
||||
function min_replies_input() {
|
||||
self::text_input( 'min-replies', __( 'Minimum replies required prior to pulling comments across', 'wp-discourse' ), 'number', 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the min-trust-level input.
|
||||
*/
|
||||
function min_trust_level_input() {
|
||||
self::text_input( 'min-trust-level', __( 'Minimum trust level required prior to pulling comments across (0-5)', 'wp-discourse' ), 'number', 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the min-score input.
|
||||
*/
|
||||
function min_score_input() {
|
||||
self::text_input( 'min-score', __( 'Minimum score required prior to pulling comments across (score = 15 points per like, 5 per reply, 5 per incoming link, 0.2 per read)', 'wp-discourse' ), 'number', 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the custom-excerpt-length input.
|
||||
*/
|
||||
function custom_excerpt_length() {
|
||||
self::text_input( 'custom-excerpt-length', __( 'Custom excerpt length in words (default: 55)', 'wp-discourse' ), 'number', 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the custom-datetime input.
|
||||
*/
|
||||
function custom_datetime_format() {
|
||||
self::text_input( 'custom-datetime-format', __( 'Custom comment meta datetime string format (default: "', 'wp-discourse' ) .
|
||||
get_option( 'date_format' ) . '").' .
|
||||
__( ' See ', 'wp-discourse' ) . '<a href="https://codex.wordpress.org/Formatting_Date_and_Time" target="_blank">' .
|
||||
__( 'this', 'wp-discourse' ) . '</a>' . __( ' for more info.', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the bypass-trust-level input.
|
||||
*/
|
||||
function bypass_trust_level_input() {
|
||||
self::text_input( 'bypass-trust-level-score', __( 'Bypass trust level check on posts with this score', 'wp-discourse' ), 'number', 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the debug-mode checkbox.
|
||||
*/
|
||||
function debug_mode_checkbox() {
|
||||
self::checkbox_input( 'debug-mode', __( '(always refresh comments)', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the use-full-post checkbox.
|
||||
*/
|
||||
function full_post_checkbox() {
|
||||
self::checkbox_input( 'full-post-content', __( 'Use the full post for content rather than an excerpt.', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs markup for the only-show-moderator-liked checkbox.
|
||||
*/
|
||||
function only_show_moderator_liked_checkbox() {
|
||||
self::checkbox_input( 'only-show-moderator-liked', __( 'Yes', 'wp-discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for a checkbox input.
|
||||
*
|
||||
* @param string $option The option name.
|
||||
* @param string $label The text for the label.
|
||||
* @param string $description The description of the settings field.
|
||||
*/
|
||||
function checkbox_input( $option, $label, $description = '' ) {
|
||||
$options = $this->options;
|
||||
if ( array_key_exists( $option, $options ) and 1 === intval( $options[ $option ] ) ) {
|
||||
$checked = 'checked="checked"';
|
||||
} else {
|
||||
$checked = '';
|
||||
}
|
||||
|
||||
?>
|
||||
<label>
|
||||
<input id='discourse_<?php echo esc_attr( $option ); ?>'
|
||||
name='discourse[<?php echo esc_attr( $option ); ?>]' type='checkbox'
|
||||
value='1' <?php echo esc_attr( $checked ); ?> />
|
||||
<?php echo esc_html( $label ); ?>
|
||||
</label>
|
||||
<p class="description"><?php echo esc_html( $description ); ?></p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the post-type select input.
|
||||
*
|
||||
* @param string $option Used to set the selected option.
|
||||
* @param array $post_types An array of available post types.
|
||||
* @param string $description The description of the settings field.
|
||||
*/
|
||||
function post_type_select_input( $option, $post_types, $description = '' ) {
|
||||
$options = $this->options;
|
||||
$allowed = array(
|
||||
'strong' => array(),
|
||||
);
|
||||
|
||||
echo "<select multiple id='discourse_allowed_post_types' class='discourse-allowed-types' name='discourse[allowed_post_types][]'>";
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
|
||||
if ( array_key_exists( $option, $options ) and in_array( $post_type, $options[ $option ], true ) ) {
|
||||
$value = 'selected';
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
echo '<option ' . esc_attr( $value ) . " value='" . esc_attr( $post_type ) . "'>" . esc_html( $post_type ) . '</option>';
|
||||
}
|
||||
|
||||
echo '</select>';
|
||||
echo '<p class="description">' . wp_kses( $description, $allowed ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for the categories select input.
|
||||
*
|
||||
* @param string $option The name of the option.
|
||||
* @param string $description The description of the settings field.
|
||||
*/
|
||||
function category_select( $option, $description ) {
|
||||
$options = get_option( 'discourse' );
|
||||
|
||||
$categories = DiscourseUtilities::get_discourse_categories();
|
||||
|
||||
if ( is_wp_error( $categories ) ) {
|
||||
esc_html_e( 'The category list will be synced with Discourse when you establish a connection.' , 'wp-discourse' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$selected = isset( $options['publish-category'] ) ? $options['publish-category'] : '';
|
||||
$name = "discourse[$option]";
|
||||
self::option_input( $name, $categories, $selected );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for an option input.
|
||||
*
|
||||
* @param string $name Suppies the 'name' value for the select input.
|
||||
* @param array $group The array of items to be selected.
|
||||
* @param int $selected The value of the selected option.
|
||||
*/
|
||||
function option_input( $name, $group, $selected ) {
|
||||
echo '<select id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '">';
|
||||
|
||||
foreach ( $group as $item ) {
|
||||
printf( '<option value="%s"%s>%s</option>',
|
||||
esc_attr( $item['id'] ),
|
||||
selected( $selected, $item['id'], false ),
|
||||
esc_html( $item['name'] )
|
||||
);
|
||||
}
|
||||
|
||||
echo '</select>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for an input box, defaults to outputting a text input, but
|
||||
* can be used for other types.
|
||||
*
|
||||
* @param string $option The name of the option.
|
||||
* @param string $description The description of the settings field.
|
||||
* @param null $type The type of input ('number', 'url', etc).
|
||||
* @param null $min The min value (applied to number inputs).
|
||||
*/
|
||||
function text_input( $option, $description, $type = null, $min = null ) {
|
||||
$options = $this->options;
|
||||
$allowed = array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
if ( array_key_exists( $option, $options ) ) {
|
||||
$value = $options[ $option ];
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
?>
|
||||
<input id='discourse_<?php echo esc_attr( $option ); ?>' name='discourse[<?php echo esc_attr( $option ); ?>]'
|
||||
type="<?php echo isset( $type ) ? esc_attr( $type ) : 'text'; ?>"
|
||||
<?php if ( isset( $min ) ) {
|
||||
echo 'min="' . esc_attr( $min ) . '"';
|
||||
} ?>
|
||||
value='<?php echo esc_attr( $value ); ?>' class="regular-text ltr"/>
|
||||
<p class="description"><?php echo wp_kses( $description, $allowed ); ?></p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for a text area.
|
||||
*
|
||||
* @param string $option The name of the option.
|
||||
* @param string $description The description of the settings field.
|
||||
*/
|
||||
function text_area( $option, $description ) {
|
||||
$options = $this->options;
|
||||
|
||||
if ( array_key_exists( $option, $options ) ) {
|
||||
$value = $options[ $option ];
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
?>
|
||||
<textarea cols=100 rows=6 id='discourse_<?php echo esc_attr( $option ); ?>'
|
||||
name='discourse[<?php echo esc_attr( $option ); ?>]'><?php echo esc_textarea( $value ); ?></textarea>
|
||||
<p class="description"><?php echo esc_html( $description ); ?></p>
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback for validating the 'discourse' options.
|
||||
*
|
||||
* @param array $inputs The inputs to be validated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function discourse_validate_options( $inputs ) {
|
||||
$output = array();
|
||||
foreach ( $inputs as $key => $input ) {
|
||||
$filter = 'validate_' . str_replace( '-', '_', $key );
|
||||
$output[ $key ] = apply_filters( $filter, $input );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Discourse options page to the admin menu.
|
||||
*
|
||||
* Hooks into the 'admin_menu' action.
|
||||
*/
|
||||
function discourse_admin_menu() {
|
||||
add_options_page( __( 'Discourse', 'wp-discourse' ), __( 'Discourse', 'wp-discourse' ), 'manage_options', 'discourse', array(
|
||||
$this,
|
||||
'discourse_options_page',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback for creating the Discourse options page.
|
||||
*/
|
||||
function discourse_options_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-discourse' ) );
|
||||
}
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h2>Discourse Options</h2>
|
||||
<p class="documentation-link">
|
||||
<em><?php esc_html_e( 'The WP Discourse plugin documentation can be found at ', 'wp-discourse' ); ?></em>
|
||||
<a href="https://github.com/discourse/wp-discourse/wiki">https://github.com/discourse/wp-discourse/wiki</a>
|
||||
</p>
|
||||
<form action="options.php" method="POST">
|
||||
<?php settings_fields( 'discourse' ); ?>
|
||||
<?php do_settings_sections( 'discourse' ); ?>
|
||||
<?php submit_button(); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for the 'connected' notice.
|
||||
*/
|
||||
function connection_status_notice() {
|
||||
if ( ! DiscourseUtilities::check_connection_status() ) {
|
||||
add_action( 'admin_notices', array( $this, 'disconnected' ) );
|
||||
} else {
|
||||
add_action( 'admin_notices', array( $this, 'connected' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for the 'disconnected' notice.
|
||||
*/
|
||||
function disconnected() {
|
||||
?>
|
||||
<div class="notice notice-warning is-dismissible">
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'You are not currently connected to a Discourse forum. ' .
|
||||
"To establish a connection, check your settings for 'Discourse URL', 'API Key', and 'Publishing username'. " .
|
||||
'Also, make sure that your Discourse forum is online.', 'wp-discourse' ); ?></strong>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the markup for the 'connected' notice.
|
||||
*/
|
||||
function connected() {
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'You are connected to Discourse!', 'wp-discourse' ); ?></strong>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'public' post-types minus the post-types in the 'excluded' array.
|
||||
*
|
||||
* @param array $excluded_types An array of post-types to exclude from publishing to Discourse.
|
||||
*
|
||||
* @return mixed|void
|
||||
*/
|
||||
protected function post_types_to_publish( $excluded_types = array() ) {
|
||||
$post_types = get_post_types( array( 'public' => true ) );
|
||||
foreach ( $excluded_types as $excluded ) {
|
||||
unset( $post_types[ $excluded ] );
|
||||
}
|
||||
|
||||
return apply_filters( 'discourse_post_types_to_publish', $post_types );
|
||||
}
|
||||
}
|
189
lib/discourse-comment.php
Normal file
189
lib/discourse-comment.php
Normal file
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
/**
|
||||
* Syncs Discourse comments with WordPress posts.
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\DiscourseComment;
|
||||
use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
|
||||
|
||||
/**
|
||||
* Class DiscourseComment
|
||||
*/
|
||||
class DiscourseComment {
|
||||
|
||||
/**
|
||||
* Gives access to the plugin options.
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed|void
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* DiscourseComment constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'discourse' );
|
||||
add_filter( 'comments_number', array( $this, 'comments_number' ) );
|
||||
add_filter( 'comments_template', array( $this, 'comments_template' ), 20, 1 );
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'discourse_comments_js' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the `comments.js` script.
|
||||
*
|
||||
* Hooks into 'wp_enqueue_scripts'.
|
||||
*/
|
||||
function discourse_comments_js() {
|
||||
if ( is_singular( $this->options['allowed_post_types'] ) ) {
|
||||
if ( $this->use_discourse_comments( get_the_ID() ) ) {
|
||||
wp_enqueue_script(
|
||||
'discourse-comments-js',
|
||||
WPDISCOURSE_URL . '/js/comments.js',
|
||||
array( 'jquery' ),
|
||||
get_option( 'discourse_version' ),
|
||||
true
|
||||
);
|
||||
// Localize script.
|
||||
$data = array(
|
||||
'url' => $this->options['url'],
|
||||
);
|
||||
wp_localize_script( 'discourse-comments-js', 'discourse', $data );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a post is using Discourse comments.
|
||||
*
|
||||
* @param int $postid The ID of the post.
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
protected function use_discourse_comments( $postid ) {
|
||||
if ( ( ! isset( $this->options['use-discourse-comments'] ) ) || ! $this->options['use-discourse-comments'] ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$setting = get_post_meta( $postid, 'publish_to_discourse', true );
|
||||
|
||||
return 1 === intval( $setting );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs Discourse comments to WordPress.
|
||||
*
|
||||
* @param int $postid The WordPress post id.
|
||||
*/
|
||||
function sync_comments( $postid ) {
|
||||
$discourse_options = $this->options;
|
||||
// Every 10 minutes do a json call to sync comment count and top comments.
|
||||
$last_sync = (int) get_post_meta( $postid, 'discourse_last_sync', true );
|
||||
$time = date_create()->format( 'U' );
|
||||
$debug = isset( $discourse_options['debug-mode'] ) && 1 === intval( $discourse_options['debug-mode'] );
|
||||
|
||||
if ( $debug || $last_sync + 60 * 10 < $time ) {
|
||||
$lock = 'comments_locked_for_' . $postid;
|
||||
if ( ! 'locked' === get_transient( $lock ) ) {
|
||||
set_transient( $lock, 'locked' );
|
||||
|
||||
if ( 'publish' === get_post_status( $postid ) ) {
|
||||
|
||||
$comment_count = intval( $discourse_options['max-comments'] );
|
||||
$min_trust_level = intval( $discourse_options['min-trust-level'] );
|
||||
$min_score = intval( $discourse_options['min-score'] );
|
||||
$min_replies = intval( $discourse_options['min-replies'] );
|
||||
$bypass_trust_level_score = intval( $discourse_options['bypass-trust-level-score'] );
|
||||
|
||||
$options = 'best=' . $comment_count . '&min_trust_level=' . $min_trust_level . '&min_score=' . $min_score;
|
||||
$options = $options . '&min_replies=' . $min_replies . '&bypass_trust_level_score=' . $bypass_trust_level_score;
|
||||
|
||||
if ( isset( $discourse_options['only-show-moderator-liked'] ) && 1 === intval( $discourse_options['only-show-moderator-liked'] ) ) {
|
||||
$options = $options . '&only_moderator_liked=true';
|
||||
}
|
||||
$options = $options . '&api_key=' . $discourse_options['api-key'] . '&api_username=' . $discourse_options['publish-username'];
|
||||
|
||||
$permalink = esc_url_raw( get_post_meta( $postid, 'discourse_permalink', true ) ) . '/wordpress.json?' . $options;
|
||||
$result = wp_remote_get( $permalink );
|
||||
|
||||
if ( DiscourseUtilities::validate( $result ) ) {
|
||||
|
||||
$json = json_decode( $result['body'] );
|
||||
|
||||
if ( isset( $json->posts_count ) ) {
|
||||
$posts_count = $json->posts_count - 1;
|
||||
if ( $posts_count < 0 ) {
|
||||
$posts_count = 0;
|
||||
}
|
||||
|
||||
update_post_meta( $postid, 'discourse_comments_count', $posts_count );
|
||||
update_post_meta( $postid, 'discourse_comments_raw', esc_sql( $result['body'] ) );
|
||||
update_post_meta( $postid, 'discourse_last_sync', $time );
|
||||
}
|
||||
}
|
||||
}
|
||||
delete_transient( $lock );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the comments template.
|
||||
*
|
||||
* Hooks into 'comments_template'.
|
||||
*
|
||||
* @param string $old The comments template returned by WordPress.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function comments_template( $old ) {
|
||||
global $post;
|
||||
|
||||
if ( $this->use_discourse_comments( $post->ID ) ) {
|
||||
$this->sync_comments( $post->ID );
|
||||
$options = $this->options;
|
||||
$num_wp_comments = get_comments_number();
|
||||
if ( ( isset( $options['show-existing-comments'] ) && ( 0 === intval( $options['show-existing-comments'] ) ) ) ||
|
||||
0 === intval( $num_wp_comments ) ) {
|
||||
// Only show the Discourse comments.
|
||||
return WPDISCOURSE_PATH . 'templates/comments.php';
|
||||
} else {
|
||||
// Show the Discourse comments then show the existing WP comments (in $old).
|
||||
include WPDISCOURSE_PATH . 'templates/comments.php';
|
||||
echo '<div class="discourse-existing-comments-heading">' . wp_kses_post( $options['existing-comments-heading'] ) . '</div>';
|
||||
|
||||
return $old;
|
||||
}
|
||||
}
|
||||
|
||||
// Show the existing WP comments.
|
||||
return $old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comments number.
|
||||
*
|
||||
* If Discourse comments are enabled, returns the 'discourse_comments_count', otherwise
|
||||
* returns the $count value. Hooks into 'comments_number'.
|
||||
*
|
||||
* @param int $count The comment count supplied by WordPress.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
function comments_number( $count ) {
|
||||
global $post;
|
||||
if ( $this->use_discourse_comments( $post->ID ) ) {
|
||||
$this->sync_comments( $post->ID );
|
||||
$count = get_post_meta( $post->ID, 'discourse_comments_count', true );
|
||||
if ( ! $count ) {
|
||||
$count = 'Leave a reply';
|
||||
} else {
|
||||
$count = ( 1 === intval( $count ) ) ? '1 Reply' : $count . ' Replies';
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
242
lib/discourse-publish.php
Normal file
242
lib/discourse-publish.php
Normal file
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
/**
|
||||
* Publishes a post to Discourse.
|
||||
*
|
||||
* @package WPDicourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\DiscoursePublish;
|
||||
|
||||
use WPDiscourse\Templates\HTMLTemplates as Templates;
|
||||
use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
|
||||
|
||||
/**
|
||||
* Class DiscoursePublish
|
||||
*/
|
||||
class DiscoursePublish {
|
||||
|
||||
/**
|
||||
* Gives access to the plugin options.
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed|void
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* DiscoursePublish constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'discourse' );
|
||||
|
||||
// Priority is set to 13 so that 'publish_post_after_save' is called after the meta-box is saved.
|
||||
add_action( 'save_post', array( $this, 'publish_post_after_save' ), 13, 2 );
|
||||
add_action( 'transition_post_status', array( $this, 'publish_post_after_transition' ), 10, 3 );
|
||||
add_action( 'xmlrpc_publish_post', array( $this, 'xmlrpc_publish_post_to_discourse' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a post to Discourse after its status has transitioned.
|
||||
*
|
||||
* This function is called when post status changes. Hooks into 'transition_post_status'.
|
||||
*
|
||||
* @param string $new_status New post status after an update.
|
||||
* @param string $old_status The old post status.
|
||||
* @param object $post The post object.
|
||||
*/
|
||||
function publish_post_after_transition( $new_status, $old_status, $post ) {
|
||||
$publish_to_discourse = get_post_meta( $post->ID, 'publish_to_discourse', true );
|
||||
|
||||
if ( $publish_to_discourse && 'publish' === $new_status && $this->is_valid_sync_post_type( $post->ID ) ) {
|
||||
$this->sync_to_discourse( $post->ID, $post->post_title, $post->post_content );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Published a post to Discourse after it has been saved.
|
||||
*
|
||||
* @param int $post_id The id of the post that has been saved.
|
||||
* @param object $post The Post object.
|
||||
*/
|
||||
public function publish_post_after_save( $post_id, $post ) {
|
||||
if ( wp_is_post_revision( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
$post_is_published = 'publish' === get_post_status( $post_id );
|
||||
$publish_to_discourse = get_post_meta( $post_id, 'publish_to_discourse', true );
|
||||
if ( $publish_to_discourse && $post_is_published && $this->is_valid_sync_post_type( $post_id ) ) {
|
||||
$this->sync_to_discourse( $post_id, $post->post_title, $post->post_content );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For publishing by xmlrpc.
|
||||
*
|
||||
* Hooks into 'xmlrpc_publish_post'.
|
||||
*
|
||||
* @param int $postid The post id.
|
||||
*/
|
||||
public function xmlrpc_publish_post_to_discourse( $postid ) {
|
||||
$post = get_post( $postid );
|
||||
if ( 'publish' === get_post_status( $postid ) && $this->is_valid_sync_post_type( $postid ) ) {
|
||||
update_post_meta( $postid, 'publish_to_discourse', 1 );
|
||||
$this->sync_to_discourse( $postid, $post->post_title, $post->post_content );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `sync_do_discourse_work` after getting the lock.
|
||||
*
|
||||
* @param int $postid The post id.
|
||||
* @param string $title The title.
|
||||
* @param string $raw The raw content of the post.
|
||||
*/
|
||||
public function sync_to_discourse( $postid, $title, $raw ) {
|
||||
$lock = 'publishing_locked_for_post_' . $postid;
|
||||
|
||||
// This avoids a double sync, just 1 is allowed to go through at a time.
|
||||
if ( ! 'locked' === get_transient( $lock ) ) {
|
||||
set_transient( $lock, 'locked' );
|
||||
$this->sync_to_discourse_work( $postid, $title, $raw );
|
||||
delete_transient( $lock );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs a post to Discourse.
|
||||
*
|
||||
* @param int $postid The post id.
|
||||
* @param string $title The post title.
|
||||
* @param string $raw The content of the post.
|
||||
*/
|
||||
protected function sync_to_discourse_work( $postid, $title, $raw ) {
|
||||
$discourse_id = get_post_meta( $postid, 'discourse_post_id', true );
|
||||
$options = $this->options;
|
||||
$discourse_post = get_post( $postid );
|
||||
$use_full_post = isset( $options['full-post-content'] ) && 1 === intval( $options['full-post-content'] );
|
||||
|
||||
if ( $use_full_post ) {
|
||||
$excerpt = apply_filters( 'wp_discourse_excerpt', $raw );
|
||||
} else {
|
||||
if ( has_excerpt( $postid ) ) {
|
||||
$wp_excerpt = apply_filters( 'get_the_excerpt', $discourse_post->post_excerpt );
|
||||
$excerpt = apply_filters( 'wp_discourse_excerpt', $wp_excerpt );
|
||||
} else {
|
||||
$excerpt = apply_filters( 'the_content', $raw );
|
||||
$excerpt = apply_filters( 'wp_discourse_excerpt', wp_trim_words( $excerpt, $options['custom-excerpt-length'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Trim to keep the Discourse markdown parser from treating this as code.
|
||||
$baked = trim( Templates::publish_format_html() );
|
||||
$baked = str_replace( '{excerpt}', $excerpt, $baked );
|
||||
$baked = str_replace( '{blogurl}', get_permalink( $postid ), $baked );
|
||||
$author_id = $discourse_post->post_author;
|
||||
$author = get_the_author_meta( 'display_name', $author_id );
|
||||
$baked = str_replace( '{author}', $author, $baked );
|
||||
$thumb = wp_get_attachment_image_src( get_post_thumbnail_id( $postid ), 'thumbnail' );
|
||||
$baked = str_replace( '{thumbnail}', '', $baked );
|
||||
$featured = wp_get_attachment_image_src( get_post_thumbnail_id( $postid ), 'full' );
|
||||
$baked = str_replace( '{featuredimage}', '', $baked );
|
||||
|
||||
$username = get_the_author_meta( 'discourse_username', $discourse_post->post_author );
|
||||
if ( ! $username || strlen( $username ) < 2 ) {
|
||||
$username = $options['publish-username'];
|
||||
}
|
||||
|
||||
// Get publish category of a post.
|
||||
$publish_post_category = get_post_meta( $discourse_post->ID, 'publish_post_category', true );
|
||||
$default_category = isset( $options['publish-category'] ) ? $options['publish-category'] : '';
|
||||
$category = isset( $publish_post_category ) ? $publish_post_category : $default_category;
|
||||
|
||||
if ( ! $discourse_id > 0 ) {
|
||||
$data = array(
|
||||
'wp-id' => $postid,
|
||||
'embed_url' => get_permalink( $postid ),
|
||||
'api_key' => $options['api-key'],
|
||||
'api_username' => $username,
|
||||
'title' => $title,
|
||||
'raw' => $baked,
|
||||
'category' => $category,
|
||||
'skip_validations' => 'true',
|
||||
'auto_track' => ( isset( $options['auto-track'] ) && 1 === intval( $options['auto-track'] ) ? 'true' : 'false' ),
|
||||
);
|
||||
$url = $options['url'] . '/posts';
|
||||
// Use key 'http' even if you send the request to https://.
|
||||
$post_options = array(
|
||||
'timeout' => 30,
|
||||
'method' => 'POST',
|
||||
'body' => http_build_query( $data ),
|
||||
);
|
||||
$result = wp_remote_post( $url, $post_options );
|
||||
|
||||
if ( DiscourseUtilities::validate( $result ) ) {
|
||||
$json = json_decode( $result['body'] );
|
||||
|
||||
if ( property_exists( $json, 'id' ) ) {
|
||||
$discourse_id = (int) $json->id;
|
||||
}
|
||||
|
||||
if ( isset( $discourse_id ) && $discourse_id > 0 ) {
|
||||
add_post_meta( $postid, 'discourse_post_id', $discourse_id, true );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data = array(
|
||||
'api_key' => $options['api-key'],
|
||||
'api_username' => $username,
|
||||
'post[raw]' => $baked,
|
||||
'skip_validations' => 'true',
|
||||
);
|
||||
$url = $options['url'] . '/posts/' . $discourse_id;
|
||||
$post_options = array(
|
||||
'timeout' => 30,
|
||||
'method' => 'PUT',
|
||||
'body' => http_build_query( $data ),
|
||||
);
|
||||
$result = wp_remote_post( $url, $post_options );
|
||||
|
||||
if ( DiscourseUtilities::validate( $result ) ) {
|
||||
$json = json_decode( $result['body'] );
|
||||
|
||||
if ( property_exists( $json, 'id' ) ) {
|
||||
$discourse_id = (int) $json->id;
|
||||
}
|
||||
|
||||
if ( isset( $discourse_id ) && $discourse_id > 0 ) {
|
||||
add_post_meta( $postid, 'discourse_post_id', $discourse_id, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $json->topic_slug ) ) {
|
||||
delete_post_meta( $postid, 'discourse_permalink' );
|
||||
add_post_meta( $postid, 'discourse_permalink', $options['url'] . '/t/' . $json->topic_slug . '/' . $json->topic_id, true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a post_type can be synced.
|
||||
*
|
||||
* @param null $postid The ID of the post in question.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_valid_sync_post_type( $postid = null ) {
|
||||
$allowed_post_types = $this->get_allowed_post_types();
|
||||
$current_post_type = get_post_type( $postid );
|
||||
|
||||
return in_array( $current_post_type, $allowed_post_types, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of allowed post types.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_allowed_post_types() {
|
||||
$selected_post_types = $this->options['allowed_post_types'];
|
||||
|
||||
return $selected_post_types;
|
||||
}
|
||||
}
|
174
lib/discourse-sso.php
Normal file
174
lib/discourse-sso.php
Normal file
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
/**
|
||||
* Allows for Single Sign On between between WordPress and Discourse.
|
||||
*
|
||||
* @package WPDiscourse\DiscourseSSO
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\DiscourseSSO;
|
||||
|
||||
/**
|
||||
* Class DiscourseSSO
|
||||
*/
|
||||
class DiscourseSSO {
|
||||
|
||||
/**
|
||||
* Gives access to the plugin options.
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed|void
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* DiscourseSSO constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'discourse' );
|
||||
|
||||
add_filter( 'query_vars', array( $this, 'sso_add_query_vars' ) );
|
||||
add_filter( 'login_url', array( $this, 'set_login_url' ), 10, 2 );
|
||||
add_action( 'parse_query', array( $this, 'sso_parse_request' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the login_url to be configured.
|
||||
*
|
||||
* Hooks into the 'login_url' filter. If the 'login-path' option has been set the supplied path
|
||||
* is used instead of the default WordPress login path.
|
||||
*
|
||||
* @param string $login_url The WordPress login url.
|
||||
* @param string $redirect The after-login redirect, supplied by WordPress.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function set_login_url( $login_url, $redirect ) {
|
||||
$options = get_option( 'discourse' );
|
||||
if ( $options['login-path'] ) {
|
||||
$login_url = $options['login-path'];
|
||||
|
||||
if ( ! empty( $redirect ) ) {
|
||||
return add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url );
|
||||
|
||||
} else {
|
||||
return $login_url;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $redirect ) ) {
|
||||
return add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url );
|
||||
} else {
|
||||
return $login_url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the 'sso' and 'sig' keys to the query_vars array.
|
||||
*
|
||||
* Hooks into 'query_vars'.
|
||||
*
|
||||
* @param array $vars The array of query vars.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sso_add_query_vars( $vars ) {
|
||||
$vars[] = 'sso';
|
||||
$vars[] = 'sig';
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO Request Processing from Adam Capirola : https://gist.github.com/adamcapriola/11300529.
|
||||
*
|
||||
* Enables single sign on between WordPress and Discourse.
|
||||
* Hooks into the 'parse_query' filter.
|
||||
*
|
||||
* @param WP_Query $wp The query object that parsed the query.
|
||||
*
|
||||
* @throws Exception Throws an exception it SSO helper class is not included, or the payload can't be validated against the sig.
|
||||
*/
|
||||
function sso_parse_request( $wp ) {
|
||||
|
||||
/**
|
||||
* Sync logout from Discourse to WordPress from Adam Capirola : https://meta.discourse.org/t/wordpress-integration-guide/27531.
|
||||
* To make this work, enter a URL of the form "http://my-wp-blog.com/?request=logout" in the "logout redirect"
|
||||
* field in your Discourse admin
|
||||
*/
|
||||
if ( isset( $this->options['enable-sso'] ) &&
|
||||
1 === intval( $this->options['enable-sso'] ) &&
|
||||
isset( $_GET['request'] ) && // Input var okay.
|
||||
'logout' === $_GET['request'] // Input var okay.
|
||||
) {
|
||||
|
||||
wp_logout();
|
||||
wp_redirect( $this->options['url'] );
|
||||
exit;
|
||||
}
|
||||
// End logout processing.
|
||||
if ( isset( $this->options['enable-sso'] ) &&
|
||||
1 === intval( $this->options['enable-sso'] ) &&
|
||||
array_key_exists( 'sso', $wp->query_vars ) &&
|
||||
array_key_exists( 'sig', $wp->query_vars )
|
||||
) {
|
||||
// Not logged in to WordPress, redirect to WordPress login page with redirect back to here.
|
||||
if ( ! is_user_logged_in() ) {
|
||||
|
||||
// Preserve sso and sig parameters.
|
||||
$redirect = add_query_arg( null, null );
|
||||
|
||||
// Change %0A to %0B so it's not stripped out in wp_sanitize_redirect.
|
||||
$redirect = str_replace( '%0A', '%0B', $redirect );
|
||||
|
||||
// Build login URL.
|
||||
$login = wp_login_url( esc_url_raw( $redirect ) );
|
||||
|
||||
// Redirect to login.
|
||||
wp_redirect( $login );
|
||||
exit;
|
||||
} else {
|
||||
|
||||
// Check for helper class.
|
||||
if ( ! class_exists( '\\WPDiscourse\\SSO\\Discourse_SSO' ) ) {
|
||||
echo( 'Helper class is not properly included.' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// Payload and signature.
|
||||
$payload = $wp->query_vars['sso'];
|
||||
$sig = $wp->query_vars['sig'];
|
||||
|
||||
// Change %0B back to %0A.
|
||||
$payload = urldecode( str_replace( '%0B', '%0A', urlencode( $payload ) ) );
|
||||
|
||||
// Validate signature.
|
||||
$sso_secret = $this->options['sso-secret'];
|
||||
$sso = new \WPDiscourse\SSO\Discourse_SSO( $sso_secret );
|
||||
|
||||
if ( ! ( $sso->validate( $payload, $sig ) ) ) {
|
||||
echo( 'Invalid request.' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$nonce = $sso->get_nonce( $payload );
|
||||
$current_user = wp_get_current_user();
|
||||
$params = array(
|
||||
'nonce' => $nonce,
|
||||
'name' => $current_user->display_name,
|
||||
'username' => $current_user->user_login,
|
||||
'email' => $current_user->user_email,
|
||||
'about_me' => $current_user->description,
|
||||
'external_id' => $current_user->ID,
|
||||
'avatar_url' => get_avatar_url( get_current_user_id() ),
|
||||
);
|
||||
|
||||
$q = $sso->build_login_string( $params );
|
||||
|
||||
// Redirect back to Discourse.
|
||||
wp_redirect( $this->options['url'] . '/session/sso_login?' . $q );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
85
lib/discourse.php
Normal file
85
lib/discourse.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
/**
|
||||
* Sets up the plugin.
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\Discourse;
|
||||
|
||||
/**
|
||||
* Class Discourse
|
||||
*/
|
||||
class Discourse {
|
||||
|
||||
/**
|
||||
* Sets the plugin version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $version = '0.7.0';
|
||||
|
||||
/**
|
||||
* The default options.
|
||||
*
|
||||
* The options can be accessed in any file with `get_option( 'discourse' )`.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static $options = array(
|
||||
'url' => '',
|
||||
'api-key' => '',
|
||||
'enable-sso' => 0,
|
||||
'sso-secret' => '',
|
||||
'publish-username' => 'system',
|
||||
'display-subcategories' => 0,
|
||||
'publish-category' => '',
|
||||
'auto-publish' => 0,
|
||||
'allowed_post_types' => array( 'post' ),
|
||||
'auto-track' => 1,
|
||||
'max-comments' => 5,
|
||||
'use-discourse-comments' => 0,
|
||||
'show-existing-comments' => 0,
|
||||
'min-score' => 0,
|
||||
'min-replies' => 1,
|
||||
'min-trust-level' => 1,
|
||||
'custom-excerpt-length' => 55,
|
||||
'bypass-trust-level-score' => 50,
|
||||
'debug-mode' => 0,
|
||||
'full-post-content' => 0,
|
||||
'only-show-moderator-liked' => 0,
|
||||
'login-path' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Discourse constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
load_plugin_textdomain( 'wp-discourse', false, basename( dirname( __FILE__ ) ) . '/languages' );
|
||||
|
||||
add_filter( 'user_contactmethods', array( $this, 'extend_user_profile' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the options 'discourse' and 'discourse_version'.
|
||||
*
|
||||
* Called with `register_activation_hook` from `wp-discourse.php`.
|
||||
*/
|
||||
public static function install() {
|
||||
update_option( 'discourse_version', self::$version );
|
||||
add_option( 'discourse', self::$options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds 'discourse_username' to the user_contactmethods array.
|
||||
*
|
||||
* @param array $fields The array of contact methods.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function extend_user_profile( $fields ) {
|
||||
$fields['discourse_username'] = 'Discourse Username';
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
190
lib/html-templates.php
Normal file
190
lib/html-templates.php
Normal file
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
/**
|
||||
* Returns HTML templates used for publishing to Discourse and for displaying comments on the WordPress site.
|
||||
*
|
||||
* Templates and implementation copied from @aliso's commit:
|
||||
* https://github.com/10up/wp-discourse/commit/5c9d43c4333e136204d5a3b07192f4b368c3f518.
|
||||
*
|
||||
* @link https://github.com/discourse/wp-discourse/blob/master/lib/html-templates.php
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\Templates;
|
||||
|
||||
/**
|
||||
* Class HTMLTemplates
|
||||
*/
|
||||
class HTMLTemplates {
|
||||
|
||||
/**
|
||||
* HTML template for replies.
|
||||
*
|
||||
* Can be customized from within a theme using the filter provided.
|
||||
*
|
||||
* Available tags:
|
||||
* {comments}, {discourse_url}, {discourse_url_name},
|
||||
* {topic_url}, {more_replies}, {participants}
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function replies_html() {
|
||||
ob_start();
|
||||
?>
|
||||
<div id="comments" class="comments-area">
|
||||
<h2 class="comments-title"><?php esc_html_e( 'Notable Replies', 'wp-discourse' ); ?></h2>
|
||||
<ol class="comment-list">{comments}</ol>
|
||||
<div class="respond comment-respond">
|
||||
<h3 id="reply-title" class="comment-reply-title">
|
||||
<a href="{topic_url}"><?php esc_html_e( 'Continue the discussion', 'wp-discourse' ); ?>
|
||||
</a><?php esc_html_e( ' at ', 'wp-discourse' ); ?>{discourse_url_name}
|
||||
</h3>
|
||||
<p class="more-replies">{more_replies}</p>
|
||||
<p class="comment-reply-title">{participants}</p>
|
||||
</div><!-- #respond -->
|
||||
</div>
|
||||
<?php
|
||||
$output = ob_get_clean();
|
||||
|
||||
return apply_filters( 'discourse_replies_html', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML template for no replies.
|
||||
*
|
||||
* Can be customized from within a theme using the filter provided.
|
||||
*
|
||||
* Available tags:
|
||||
* {comments}, {discourse_url}, {discourse_url_name}, {topic_url}
|
||||
*
|
||||
* @static
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function no_replies_html() {
|
||||
ob_start();
|
||||
?>
|
||||
<div id="comments" class="comments-area">
|
||||
<div class="respond comment-respond">
|
||||
<h3 id="reply-title" class="comment-reply-title"><a href="{topic_url}">
|
||||
<?php esc_html_e( 'Start the discussion', 'wp-discourse' ); ?>
|
||||
</a><?php esc_html_e( ' at ', 'wp-discourse' ); ?>{discourse_url_name}</h3>
|
||||
</div><!-- #respond -->
|
||||
</div>
|
||||
<?php
|
||||
$output = ob_get_clean();
|
||||
|
||||
return apply_filters( 'discourse_no_replies_html', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* The template that is displayed in the comments section after a post is created
|
||||
* with bad credentials.
|
||||
* This template is displayed in the comments section when there is no `discourse_permalink`
|
||||
* index in the response returned from `Discourse::sync_to_discourse_work`
|
||||
*
|
||||
* Can be customized in the theme using the filter provided.
|
||||
*
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function bad_response_html() {
|
||||
ob_start();
|
||||
?>
|
||||
<div class="respond comment-respond">
|
||||
<div class="comment-reply-title discourse-no-connection-notice">
|
||||
<p><?php esc_html_e( 'Comments are not enabled for this post.', 'wp-discourse' ); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
$output = ob_get_clean();
|
||||
|
||||
return apply_filters( 'discourse_no_connection_html', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML template for each comment
|
||||
*
|
||||
* Can be customized from within a theme using the filter provided.
|
||||
*
|
||||
* Available tags:
|
||||
* {discourse_url}, {discourse_url_name}, {topic_url},
|
||||
* {avatar_url}, {user_url}, {username}, {fullname},
|
||||
* {comment_body}, {comment_created_at}, {comment_url}
|
||||
*
|
||||
* @static
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function comment_html() {
|
||||
ob_start();
|
||||
?>
|
||||
<li class="comment even thread-even depth-1">
|
||||
<article class="comment-body">
|
||||
<footer class="comment-meta">
|
||||
<div class="comment-author vcard">
|
||||
<img alt="" src="{avatar_url}" class="avatar avatar-64 photo avatar-default" height="64"
|
||||
width="64">
|
||||
<b class="fn"><a href="{topic_url}" rel="external" class="url">{fullname}</a></b>
|
||||
<span class="says">says:</span>
|
||||
</div>
|
||||
<!-- .comment-author -->
|
||||
<div class="comment-metadata">
|
||||
<time pubdate="" datetime="{comment_created_at}">{comment_created_at}</time>
|
||||
</div>
|
||||
<!-- .comment-metadata -->
|
||||
</footer>
|
||||
<!-- .comment-meta -->
|
||||
<div class="comment-content">{comment_body}</div>
|
||||
<!-- .comment-content -->
|
||||
</article>
|
||||
<!-- .comment-body -->
|
||||
</li>
|
||||
<?php
|
||||
$output = ob_get_clean();
|
||||
|
||||
return apply_filters( 'discourse_comment_html', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML template for each participant
|
||||
*
|
||||
* Can be customized from within a theme using the filter provided.
|
||||
*
|
||||
* Available tags:
|
||||
* {discourse_url}, {discourse_url_name}, {topic_url},
|
||||
* {avatar_url}, {user_url}, {username}
|
||||
*
|
||||
* @static
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function participant_html() {
|
||||
ob_start();
|
||||
?>
|
||||
<img alt="" src="{avatar_url}" class="avatar avatar-25 photo avatar-default" height="25"
|
||||
width="25">
|
||||
<?php
|
||||
$output = ob_get_clean();
|
||||
|
||||
return apply_filters( 'discourse_participant_html', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML template for published byline
|
||||
*
|
||||
* Can be customized from within a theme using the filter provided.
|
||||
*
|
||||
* Available tags:
|
||||
* {excerpt}, {blogurl}, {author}, {thumbnail}, {featuredimage}
|
||||
*
|
||||
* @static
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function publish_format_html() {
|
||||
ob_start();
|
||||
?>
|
||||
<small>Originally published at: {blogurl}</small><br>{excerpt}
|
||||
<?php
|
||||
$output = ob_get_clean();
|
||||
|
||||
return apply_filters( 'discourse_publish_format_html', $output );
|
||||
}
|
||||
}
|
144
lib/meta-box.php
Normal file
144
lib/meta-box.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
/**
|
||||
* Adds a Discourse Publish meta box to posts that may be published to Discourse.
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\MetaBox;
|
||||
|
||||
use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
|
||||
|
||||
/**
|
||||
* Class MetaBox
|
||||
*/
|
||||
class MetaBox {
|
||||
|
||||
/**
|
||||
* Gives access to the plugin options.
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed|void
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* MetaBox constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->options = get_option( 'discourse' );
|
||||
add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
|
||||
add_action( 'save_post', array( $this, 'save_meta_box' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a meta box for the allowed post types.
|
||||
*
|
||||
* @param string $post_type The post_type of the current post.
|
||||
*/
|
||||
public function add_meta_box( $post_type ) {
|
||||
if ( isset( $this->options['allowed_post_types'] ) &&
|
||||
in_array( $post_type, $this->options['allowed_post_types'], true )
|
||||
) {
|
||||
add_meta_box( 'discourse-publish-meta-box', esc_html__( 'Publish to Discourse' ), array(
|
||||
$this,
|
||||
'render_meta_box',
|
||||
), null, 'side', 'high', null );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback function for creating the meta box.
|
||||
*
|
||||
* @param object $post The current Post object.
|
||||
*/
|
||||
public function render_meta_box( $post ) {
|
||||
$categories = DiscourseUtilities::get_discourse_categories();
|
||||
|
||||
if ( is_wp_error( $categories ) ) {
|
||||
$selected_category = null;
|
||||
$publish_to_discourse = 0;
|
||||
} elseif ( ! get_post_meta( $post->ID, 'has_been_saved', true ) ) {
|
||||
|
||||
// If the post has not yet been saved, use the default setting. If it has been saved use the meta value.
|
||||
$selected_category = isset( $this->options['publish-category'] ) ? intval( $this->options['publish-category'] ) : 1;
|
||||
$publish_to_discourse = isset( $this->options['auto-publish'] ) ? intval( $this->options['auto-publish'] ) : 0;
|
||||
} else {
|
||||
|
||||
$selected_category = get_post_meta( $post->ID, 'publish_post_category', true );
|
||||
$publish_to_discourse = get_post_meta( $post->ID, 'publish_to_discourse', true );
|
||||
}
|
||||
|
||||
wp_nonce_field( 'publish_to_discourse', 'publish_to_discourse_nonce' );
|
||||
?>
|
||||
|
||||
<label for="publish_to_discourse"><?php esc_html_e( 'Publish post to Discourse:', 'wp-discourse' ); ?>
|
||||
<input type="checkbox" name="publish_to_discourse" id="publish_to_discourse" value="1"
|
||||
<?php checked( $publish_to_discourse ); ?> >
|
||||
</label>
|
||||
<br>
|
||||
<label for="publish_post_category"><?php esc_html_e( 'Category to publish to:', 'wp-discourse' ); ?>
|
||||
|
||||
<?php if ( is_null( $selected_category ) ) : ?>
|
||||
<div class="warning">
|
||||
<p>
|
||||
<?php
|
||||
esc_html_e( "The Discourse categories list is not currently available. To publish this post to Discourse, please check the wp-discourse settings for 'Discourse URL', 'API Key', and 'Publishing username'. Also, make sure that your Discourse forum is online.", 'wp-discourse' );
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
|
||||
<select name="publish_post_category" id="publish_post_category">
|
||||
<?php foreach ( $categories as $category ) : ?>
|
||||
<option
|
||||
value="<?php echo( esc_attr( $category['id'] ) ); ?>"
|
||||
<?php selected( $selected_category, $category['id'] ); ?>>
|
||||
<?php echo( esc_html( $category['name'] ) ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</label>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the nonce and saves the meta data.
|
||||
*
|
||||
* @param int $post_id The id of the current post.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function save_meta_box( $post_id ) {
|
||||
if ( ! isset( $_POST['publish_to_discourse_nonce'] ) || // Input var okay.
|
||||
! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['publish_to_discourse_nonce'] ) ), 'publish_to_discourse' ) // Input var okay.
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
||||
return 0;
|
||||
}
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Indicate that the post has been saved so that the meta-box gets its values from the meta-data instead of the defaults.
|
||||
update_post_meta( $post_id, 'has_been_saved', 1 );
|
||||
|
||||
if ( isset( $_POST['publish_post_category'] ) ) { // Input var okay.
|
||||
update_post_meta( $post_id, 'publish_post_category', intval( wp_unslash( $_POST['publish_post_category'] ) ) ); // Input var okay.
|
||||
}
|
||||
|
||||
if ( isset( $_POST['publish_to_discourse'] ) ) { // Input var okay.
|
||||
update_post_meta( $post_id, 'publish_to_discourse', intval( wp_unslash( $_POST['publish_to_discourse'] ) ) ); // Input var okay.
|
||||
} else {
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 0 );
|
||||
}
|
||||
|
||||
return $post_id;
|
||||
}
|
||||
}
|
61
lib/plugin-support/woocommerce-support.php
Normal file
61
lib/plugin-support/woocommerce-support.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
/**
|
||||
* Adds limitied support for the WooCommerce plugin.
|
||||
*
|
||||
* This file will soon be moved into its own plugin.
|
||||
*
|
||||
* @link https://github.com/discourse/wp-discourse/blob/master/lib/plugin-support/woocommerce_support.php
|
||||
* @package WPDiscourse\PluginSupport
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\PluginSupport;
|
||||
|
||||
/**
|
||||
* Class WooCommerceSupport
|
||||
*/
|
||||
class WooCommerceSupport {
|
||||
|
||||
/**
|
||||
* WooCommerceSupport constructor.
|
||||
*/
|
||||
function __construct() {
|
||||
add_filter( 'woocommerce_login_redirect', array( $this, 'set_redirect' ) );
|
||||
add_filter( 'woocommerce_product_review_count', array( $this, 'comments_number' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the WooCommerce comments count with the Discourse comments count.
|
||||
*
|
||||
* @param int $count The comments count returned from WooCommerce.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function comments_number( $count ) {
|
||||
global $post;
|
||||
$options = get_option( 'discourse' );
|
||||
if ( array_key_exists( 'allowed_post_types', $options ) && in_array( 'product', $options['allowed_post_types'], true ) ) {
|
||||
$count = get_post_meta( $post->ID, 'discourse_comments_count', true );
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the login redirect so that it can include the query parameters required for single sign on with Discourse.
|
||||
*
|
||||
* @param string $redirect The redirect URL supplied by WooCommerce.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function set_redirect( $redirect ) {
|
||||
if ( isset( $_GET['redirect_to'] ) && esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) ) { // Input var okay.
|
||||
$redirect = esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ); // Input var okay.
|
||||
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
return $redirect;
|
||||
}
|
||||
}
|
567
lib/settings-validator.php
Normal file
567
lib/settings-validator.php
Normal file
|
@ -0,0 +1,567 @@
|
|||
<?php
|
||||
/**
|
||||
* Validation methods for the settings page.
|
||||
*
|
||||
* @link https://github.com/discourse/wp-discourse/blob/master/lib/settings-validator.php
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\Validator;
|
||||
|
||||
/**
|
||||
* Class SettingsValidator
|
||||
*
|
||||
* @package WPDiscourse\Validator
|
||||
*/
|
||||
class SettingsValidator {
|
||||
|
||||
/**
|
||||
* Indicates whether or not SSO is enabled.
|
||||
*
|
||||
* @access protected
|
||||
* @var bool
|
||||
*/
|
||||
protected $sso_enabled = false;
|
||||
|
||||
/**
|
||||
* Indicates whether or not 'use_discourse_comments' is enabled.
|
||||
*
|
||||
* @access protected
|
||||
* @var bool
|
||||
*/
|
||||
protected $use_discourse_comments = false;
|
||||
|
||||
/**
|
||||
* SettingsValidator constructor.
|
||||
*
|
||||
* Adds the callback function for each of the validator filters that are applied
|
||||
* in `admin.php`.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'validate_url', array( $this, 'validate_url' ) );
|
||||
add_filter( 'validate_api_key', array( $this, 'validate_api_key' ) );
|
||||
add_filter( 'validate_publish_username', array(
|
||||
$this,
|
||||
'validate_publish_username',
|
||||
) );
|
||||
add_filter( 'validate_publish_category', array(
|
||||
$this,
|
||||
'validate_publish_category',
|
||||
) );
|
||||
add_filter( 'validate_publish_category_update', array(
|
||||
$this,
|
||||
'validate_publish_category_update',
|
||||
) );
|
||||
add_filter( 'validate_full_post_content', array(
|
||||
$this,
|
||||
'validate_full_post_content',
|
||||
) );
|
||||
add_filter( 'validate_auto_publish', array(
|
||||
$this,
|
||||
'validate_auto_publish',
|
||||
) );
|
||||
add_filter( 'validate_auto_track', array( $this, 'validate_auto_track' ) );
|
||||
add_filter( 'validate_allowed_post_types', array(
|
||||
$this,
|
||||
'validate_allowed_post_types',
|
||||
) );
|
||||
add_filter( 'validate_use_discourse_comments', array(
|
||||
$this,
|
||||
'validate_use_discourse_comments',
|
||||
) );
|
||||
add_filter( 'validate_show_existing_comments', array(
|
||||
$this,
|
||||
'validate_show_existing_comments',
|
||||
) );
|
||||
add_filter( 'validate_existing_comments_heading', array(
|
||||
$this,
|
||||
'validate_existing_comments_heading',
|
||||
) );
|
||||
add_filter( 'validate_max_comments', array(
|
||||
$this,
|
||||
'validate_max_comments',
|
||||
) );
|
||||
add_filter( 'validate_min_replies', array(
|
||||
$this,
|
||||
'validate_min_replies',
|
||||
) );
|
||||
add_filter( 'validate_min_score', array( $this, 'validate_min_score' ) );
|
||||
add_filter( 'validate_min_trust_level', array(
|
||||
$this,
|
||||
'validate_min_trust_level',
|
||||
) );
|
||||
add_filter( 'validate_bypass_trust_level_score', array(
|
||||
$this,
|
||||
'validate_bypass_trust_level_score',
|
||||
) );
|
||||
add_filter( 'validate_custom_excerpt_length', array(
|
||||
$this,
|
||||
'validate_custom_excerpt_length',
|
||||
) );
|
||||
add_filter( 'validate_custom_datetime_format', array(
|
||||
$this,
|
||||
'validate_custom_datetime_format',
|
||||
) );
|
||||
add_filter( 'validate_only_show_moderator_liked', array(
|
||||
$this,
|
||||
'validate_only_show_moderator_liked',
|
||||
) );
|
||||
add_filter( 'validate_display_subcategories', array(
|
||||
$this,
|
||||
'validate_display_subcategories',
|
||||
) );
|
||||
add_filter( 'validate_debug_mode', array( $this, 'validate_debug_mode' ) );
|
||||
add_filter( 'validate_enable_sso', array( $this, 'validate_enable_sso' ) );
|
||||
add_filter( 'validate_sso_secret', array( $this, 'validate_sso_secret' ) );
|
||||
add_filter( 'validate_login_path', array( $this, 'validate_login_path' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the Discourse URL.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_url( $input ) {
|
||||
$regex = '/^(http:|https:)/';
|
||||
|
||||
// Make sure the url starts with a valid protocol.
|
||||
if ( ! preg_match( $regex, $input ) ) {
|
||||
add_settings_error( 'discourse', 'discourse_url', __( 'The Discourse URL needs to begin with either \'http:\' or \'https:\'.' ) );
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( filter_var( $input, FILTER_VALIDATE_URL ) ) {
|
||||
return untrailingslashit( esc_url_raw( $input ) );
|
||||
} else {
|
||||
add_settings_error( 'discourse', 'discourse_url', __( 'The Discourse URL you provided is not a valid URL.', 'wp-discourse' ) );
|
||||
|
||||
return untrailingslashit( esc_url_raw( $input ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the api key.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_api_key( $input ) {
|
||||
$regex = '/^\s*([0-9]*[a-z]*|[a-z]*[0-9]*)*\s*$/';
|
||||
|
||||
if ( empty( $input ) ) {
|
||||
add_settings_error( 'discourse', 'api_key', __( 'You must provide an API key.', 'wp-discourse' ) );
|
||||
|
||||
return '';
|
||||
|
||||
} elseif ( preg_match( $regex, $input ) ) {
|
||||
return trim( $input );
|
||||
|
||||
} else {
|
||||
add_settings_error( 'discourse', 'api_key', __( 'The API key you provided is not valid.', 'wp-discourse' ) );
|
||||
|
||||
return $this->sanitize_text( $input );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the publish_username.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_publish_username( $input ) {
|
||||
if ( ! empty( $input ) ) {
|
||||
return $this->sanitize_text( $input );
|
||||
} else {
|
||||
add_settings_error( 'discourse', 'publish_username', __( 'You must provide a Discourse username with which to publish the posts', 'wp-discourse' ) );
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validated the 'display_subcategories' checkbox.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_display_subcategories( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'publish_category' select input.
|
||||
*
|
||||
* Returns the category id.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_publish_category( $input ) {
|
||||
return $this->sanitize_int( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'publish_category_update' checkbox.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_publish_category_update( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'full_post_content' checkbox.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_full_post_content( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'auto_publish' checkbox.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_auto_publish( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'auto_track' checkbox.
|
||||
*
|
||||
* @param string $input The input to be validates.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_auto_track( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'allowed_post_types' multi-select.
|
||||
*
|
||||
* @param array $input The array of allowed post-types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validate_allowed_post_types( $input ) {
|
||||
$output = array();
|
||||
foreach ( $input as $post_type ) {
|
||||
$output[] = sanitize_text_field( $post_type );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'use_discourse_comments' checkbox.
|
||||
*
|
||||
* If this function is called, it sets the 'use_discourse_comments' property to true. This makes it possible
|
||||
* to only show warnings for the comment settings if Discourse is being used for comments.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_use_discourse_comments( $input ) {
|
||||
$this->use_discourse_comments = true;
|
||||
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'show_existing_comments' checkbox.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_show_existing_comments( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'existing_comments_heading' input.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_existing_comments_heading( $input ) {
|
||||
return $this->sanitize_html( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'max_comments' number input.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validate_max_comments( $input ) {
|
||||
return $this->validate_int( $input, 'max_comments', 1, null,
|
||||
__( 'The max visible comments setting requires a positive integer.', 'wp-discourse' ),
|
||||
$this->use_discourse_comments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'min_replies' number input.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validate_min_replies( $input ) {
|
||||
return $this->validate_int( $input, 'min_replies', 0, null,
|
||||
__( 'The min number of replies setting requires a number greater than or equal to 0.', 'wp-discourse' ),
|
||||
$this->use_discourse_comments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'min_score' number input.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validate_min_score( $input ) {
|
||||
return $this->validate_int( $input, 'min_score', 0, null,
|
||||
__( 'The min score of posts setting requires a number greater than or equal to 0.', 'wp-discourse' ),
|
||||
$this->use_discourse_comments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'min_trust_level' number input.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validate_min_trust_level( $input ) {
|
||||
return $this->validate_int( $input, 'min_trust_level', 0, 5,
|
||||
__( 'The trust level setting requires a number between 0 and 5.', 'wp-discourse' ),
|
||||
$this->use_discourse_comments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'bypass_trust_level_score' number input.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validate_bypass_trust_level_score( $input ) {
|
||||
return $this->validate_int( $input, 'bypass_trust_level', 0, null,
|
||||
__( 'The bypass trust level score setting requires an integer greater than or equal to 0.', 'wp-discourse' ),
|
||||
$this->use_discourse_comments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'custom_excerpt_length' number input.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validate_custom_excerpt_length( $input ) {
|
||||
return $this->validate_int( $input, 'excerpt_length', 1, null,
|
||||
__( 'The custom excerpt length setting requires a positive integer.', 'wp-discourse' ),
|
||||
$this->use_discourse_comments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'custom_date_time' text input.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_custom_datetime_format( $input ) {
|
||||
return sanitize_text_field( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'only_show_moderator_liked' checkbox.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_only_show_moderator_liked( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'debug_mode' checkbox.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_debug_mode( $input ) {
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validated the 'enable_sso'checkbox.
|
||||
*
|
||||
* This function is only called if the checkbox is checked. It sets the `sso_enabled` property to true.
|
||||
* This allows sso validation notices to only be displayed if sso is enabled.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function validate_enable_sso( $input ) {
|
||||
$this->sso_enabled = true;
|
||||
|
||||
return $this->sanitize_checkbox( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'sso_secret' text input.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_sso_secret( $input ) {
|
||||
if ( strlen( sanitize_text_field( $input ) ) >= 10 ) {
|
||||
return sanitize_text_field( $input );
|
||||
|
||||
// Only add a settings error if sso is enabled, otherwise just sanitize the input.
|
||||
} elseif ( $this->sso_enabled ) {
|
||||
add_settings_error( 'discourse', 'sso_secret', __( 'The SSO secret key setting must be at least 10 characters long.', 'wp-discourse' ) );
|
||||
|
||||
return sanitize_text_field( $input );
|
||||
|
||||
} else {
|
||||
return sanitize_text_field( $input );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'login_path' text input.
|
||||
*
|
||||
* @param string $input The input to be validated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validate_login_path( $input ) {
|
||||
if ( $this->sso_enabled && $input ) {
|
||||
|
||||
$regex = '/^\/([a-z0-9\-]+)*(\/[a-z0-9\-]+)*(\/)?$/';
|
||||
if ( ! preg_match( $regex, $input ) ) {
|
||||
add_settings_error( 'discourse', 'login_path', __( 'The path to login page setting needs to be a valid file path, starting with \'/\'.', 'wp-discourse' ) );
|
||||
|
||||
return $this->sanitize_text( $input );
|
||||
|
||||
}
|
||||
|
||||
// It's valid.
|
||||
return $this->sanitize_text( $input );
|
||||
}
|
||||
|
||||
// Sanitize, but don't validate. SSO is not enabled.
|
||||
return $this->sanitize_text( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper methods
|
||||
******************************/
|
||||
|
||||
/**
|
||||
* A helper method to sanitize text inputs.
|
||||
*
|
||||
* @param string $input The input to be sanitized.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitize_text( $input ) {
|
||||
return sanitize_text_field( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to sanitize the value returned from checkbox inputs.
|
||||
*
|
||||
* @param string $input The value returned from the checkbox.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function sanitize_checkbox( $input ) {
|
||||
return 1 === intval( $input ) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to sanitize HTML.
|
||||
*
|
||||
* @param string $input HTML input to be sanitized.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitize_html( $input ) {
|
||||
return wp_kses_post( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to sanitize an int.
|
||||
*
|
||||
* @param mixed|int $input The input to be validated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function sanitize_int( $input ) {
|
||||
return intval( $input );
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to validate and sanitize integers.
|
||||
*
|
||||
* @param int $input The input to be validated.
|
||||
* @param string $option_id The option being validated.
|
||||
* @param null $min The minimum allowed value.
|
||||
* @param null $max The maximum allowed value.
|
||||
* @param string $error_message The error message to return.
|
||||
* @param bool $add_error Whether or not to add a setting error.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function validate_int( $input, $option_id, $min = null, $max = null, $error_message = '', $add_error = false ) {
|
||||
$options = array();
|
||||
|
||||
if ( isset( $min ) ) {
|
||||
$options['min_range'] = $min;
|
||||
}
|
||||
if ( isset( $max ) ) {
|
||||
$options['max_range'] = $max;
|
||||
}
|
||||
|
||||
if ( filter_var( $input, FILTER_VALIDATE_INT, array( 'options' => $options ) ) === false ) {
|
||||
if ( $add_error ) {
|
||||
add_settings_error( 'discourse', $option_id, $error_message );
|
||||
|
||||
return filter_var( $input, FILTER_SANITIZE_NUMBER_INT );
|
||||
}
|
||||
|
||||
// The input is not valid, but the setting's section is not being used, sanitize the input and return it.
|
||||
return filter_var( $input, FILTER_SANITIZE_NUMBER_INT );
|
||||
} else {
|
||||
// Valid input.
|
||||
return filter_var( $input, FILTER_SANITIZE_NUMBER_INT );
|
||||
}
|
||||
}
|
||||
}
|
92
lib/sso.php
Normal file
92
lib/sso.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/**
|
||||
* Single-sign-on for Discourse via PHP
|
||||
*
|
||||
* @link https://github.com/ArmedGuy/discourse_sso_php
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\SSO;
|
||||
|
||||
/**
|
||||
* Class Discourse_SSO
|
||||
*/
|
||||
class Discourse_SSO {
|
||||
|
||||
/**
|
||||
* The SSO secret key.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $sso_secret;
|
||||
|
||||
/**
|
||||
* Discourse_SSO constructor.
|
||||
*
|
||||
* @param string $secret The SSO secret key.
|
||||
*/
|
||||
function __construct( $secret ) {
|
||||
$this->sso_secret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the payload against the sig.
|
||||
*
|
||||
* @param string $payload A Base64 encoded string.
|
||||
* @param string $sig HMAC-SHA256 of $sso_secret, $payload should be equal to $sig.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate( $payload, $sig ) {
|
||||
$payload = urldecode( $payload );
|
||||
if ( hash_hmac( 'sha256', $payload, $this->sso_secret ) === $sig ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nonce from the payload.
|
||||
*
|
||||
* @param string $payload A Base64 encoded string.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception Thrown when the nonce in not found in the payload.
|
||||
*/
|
||||
public function get_nonce( $payload ) {
|
||||
$payload = urldecode( $payload );
|
||||
$query = array();
|
||||
parse_str( base64_decode( $payload ), $query );
|
||||
if ( isset( $query['nonce'] ) ) {
|
||||
return $query['nonce'];
|
||||
} else {
|
||||
throw new Exception( 'Nonce not found in payload!' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the sso-login query params that are sent to Discourse.
|
||||
*
|
||||
* @param array $params The array of parameters to send.
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception Thrown when the required params aren't present.
|
||||
*/
|
||||
public function build_login_string( $params ) {
|
||||
if ( ! isset( $params['external_id'] ) ) {
|
||||
throw new Exception( "Missing required parameter 'external_id'" );
|
||||
}
|
||||
if ( ! isset( $params['nonce'] ) ) {
|
||||
throw new Exception( "Missing required parameter 'nonce'" );
|
||||
}
|
||||
if ( ! isset( $params['email'] ) ) {
|
||||
throw new Exception( "Missing required parameter 'email'" );
|
||||
}
|
||||
$payload = base64_encode( http_build_query( $params ) );
|
||||
$sig = hash_hmac( 'sha256', $payload, $this->sso_secret );
|
||||
|
||||
return http_build_query( array( 'sso' => $payload, 'sig' => $sig ) );
|
||||
}
|
||||
}
|
144
lib/utilities.php
Normal file
144
lib/utilities.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
/**
|
||||
* Static utility functions used throughout the plugin.
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
namespace WPDiscourse\Utilities;
|
||||
|
||||
/**
|
||||
* Class Utilities
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
class Utilities {
|
||||
|
||||
/**
|
||||
* Checks the connection status to Discourse.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function check_connection_status() {
|
||||
$options = get_option( 'discourse' );
|
||||
$url = array_key_exists( 'url', $options ) ? $options['url'] : '';
|
||||
$url = add_query_arg( array(
|
||||
'api_key' => array_key_exists( 'api-key', $options ) ? $options['api-key'] : '',
|
||||
'api_username' => array_key_exists( 'publish-username', $options ) ? $options['publish-username'] : '',
|
||||
), $url . '/users/' . $options['publish-username'] . '.json' );
|
||||
|
||||
$url = esc_url_raw( $url );
|
||||
$response = wp_remote_get( $url );
|
||||
|
||||
return self::validate( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the response from `wp_remote_get` or `wp_remote_post`.
|
||||
*
|
||||
* @param array $response The response from `wp_remote_get` or `wp_remote_post`.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function validate( $response ) {
|
||||
// There will be a WP_Error if the server can't be accessed.
|
||||
if ( is_wp_error( $response ) ) {
|
||||
error_log( $response->get_error_message() );
|
||||
|
||||
return 0;
|
||||
|
||||
// There is a response from the server, but it's not what we're looking for.
|
||||
} elseif ( intval( wp_remote_retrieve_response_code( $response ) ) !== 200 ) {
|
||||
$error_message = wp_remote_retrieve_response_message( $response );
|
||||
error_log( 'There has been a problem accessing your Discourse forum. Error Message: ' . $error_message );
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
// Valid response.
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user's Discourse homepage.
|
||||
*
|
||||
* @param string $url The base URL of the Discourse forum.
|
||||
* @param object $post The Post object.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function homepage( $url, $post ) {
|
||||
return $url . '/users/' . strtolower( $post->username );
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes the value for `$size` into the template.
|
||||
*
|
||||
* @param string $template The avatar template.
|
||||
* @param int $size The size of the avarar.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function avatar( $template, $size ) {
|
||||
return str_replace( '{size}', $size, $template );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces relative image src with absolute.
|
||||
*
|
||||
* @param string $url The base url of the forum.
|
||||
* @param string $content The content to be checked.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function convert_relative_img_src_to_absolute( $url, $content ) {
|
||||
if ( preg_match( "/<img\s*src\s*=\s*[\'\"]?(https?:)?\/\//i", $content ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$search = '#<img src="((?!\s*[\'"]?(?:https?:)?\/\/)\s*([\'"]))?#';
|
||||
$replace = "<img src=\"{$url}$1";
|
||||
|
||||
return preg_replace( $search, $replace, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Discourse categories.
|
||||
*
|
||||
* @return array|mixed|object|\WP_Error|WP_Error
|
||||
*/
|
||||
public static function get_discourse_categories() {
|
||||
$options = get_option( 'discourse' );
|
||||
$url = add_query_arg( array(
|
||||
'api_key' => $options['api-key'],
|
||||
'api_username' => $options['publish-username'],
|
||||
), $options['url'] . '/site.json' );
|
||||
$force_update = isset( $options['publish-category-update'] ) ? $options['publish-category-update'] : '0';
|
||||
$remote = get_transient( 'discourse_settings_categories_cache' );
|
||||
$cache = $remote;
|
||||
if ( empty( $remote ) || $force_update ) {
|
||||
$remote = wp_remote_get( $url );
|
||||
if ( ! self::validate( $remote ) ) {
|
||||
if ( ! empty( $cache ) ) {
|
||||
return $cache;
|
||||
}
|
||||
return new \WP_Error( 'connection_not_established', 'There was an error establishing a connection with Discourse' );
|
||||
}
|
||||
$remote = json_decode( wp_remote_retrieve_body( $remote ), true );
|
||||
if ( array_key_exists( 'categories', $remote ) ) {
|
||||
$remote = $remote['categories'];
|
||||
if ( ! isset( $options['display-subcategories'] ) || 0 === intval( $options['display-subcategories'] ) ) {
|
||||
foreach ( $remote as $category => $values ) {
|
||||
if ( array_key_exists( 'parent_category_id', $values ) ) {
|
||||
unset( $remote[ $category ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
set_transient( 'discourse_settings_categories_cache', $remote, HOUR_IN_SECONDS );
|
||||
} else {
|
||||
return new \WP_Error( 'key_not_found', 'The categories key was not found in the response from Discourse.' );
|
||||
}
|
||||
}
|
||||
return $remote;
|
||||
}
|
||||
}
|
14
phpunit.xml.dist
Normal file
14
phpunit.xml.dist
Normal file
|
@ -0,0 +1,14 @@
|
|||
<phpunit
|
||||
bootstrap="tests/bootstrap.php"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite>
|
||||
<directory prefix="test-" suffix=".php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
66
readme.txt
Normal file
66
readme.txt
Normal file
|
@ -0,0 +1,66 @@
|
|||
=== WP Discourse ===
|
||||
Contributors: cdck, retiehs, samsaffron, scossar, techapj
|
||||
Tags: discourse, forum, comments, sso
|
||||
Requires at least: 4.4
|
||||
Tested up to: 4.5.2
|
||||
Stable tag: trunk
|
||||
License: GPLv2 or later
|
||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
WP Discourse allows you to use Discourse as a community engine for your WordPress website.
|
||||
|
||||
== Description ==
|
||||
|
||||
The WP Discourse plugin acts as an interface between your WordPress site and your
|
||||
[Discourse](http://www.discourse.org/) forum.
|
||||
|
||||
It allows you to:
|
||||
|
||||
- Publish WordPress posts to Discourse
|
||||
- Use Discourse to generate comments and discussion for your WordPress posts
|
||||
- Select which comments are to be displayed on the WordPress site based on post score and commenter trust level
|
||||
- Use your WordPress site as the Single Sign On provider for your Discourse forum
|
||||
|
||||
== Installation ==
|
||||
|
||||
#### From your WordPress dashboard
|
||||
|
||||
1. Visit 'Plugins > Add New'
|
||||
2. Search for 'WP Discourse'
|
||||
3. Activate WP Discourse from your Plugins page
|
||||
|
||||
#### From wordpress.org
|
||||
|
||||
1. Download WP Discourse
|
||||
2. Upload the 'wp-discourse' directory to your '/wp-content/plugins/' directory
|
||||
3. Activate WP Discourse from your Plugins page
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
= Does this plugin install Discourse for me? =
|
||||
|
||||
No this plugin acts as an interface between Discourse and WordPress. For it to work you will need to first set up
|
||||
Discourse forum. You can install Discourse for yourself following either of these guides:
|
||||
|
||||
- [Install Discourse in Under 30 Minutes](https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md)
|
||||
- [How to use the Discourse One-Click Application on DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-use-the-discourse-one-click-application-on-digitalocean)
|
||||
|
||||
= Is it possible to customize the comment templates?
|
||||
|
||||
Yes, the html templates used for publishing posts on Discourse and for displaying comments on WordPress can be customized in your theme.
|
||||
This is done by hooking into the filters that are applied to each template.
|
||||
|
||||
For more details on template customization, take a look at this section of our wiki: [Template Customization](https://github.com/discourse/wp-discourse/wiki/Template-Customization)
|
||||
|
||||
== Screenshots ==
|
||||
|
||||
1. Select whether a post is to be published to Discourse, and what category it is to be published into.
|
||||
|
||||
2. A WordPress posts with no comments.
|
||||
|
||||
3. Adding a comment on the Discourse forum.
|
||||
|
||||
4. The comment appears on WordPress.
|
||||
|
||||
|
||||
== Changelog ==
|
103
templates/comments.php
Executable file
103
templates/comments.php
Executable file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
/**
|
||||
* The template for comments.
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
use WPDiscourse\Templates\HTMLTemplates as Templates;
|
||||
use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
|
||||
|
||||
$custom = get_post_custom();
|
||||
|
||||
// If, when a new post is published to Discourse, there is not a valid response from
|
||||
// the forum, the `discourse_permalink` key will not be set. Display the `bad_response_html` template.
|
||||
if ( ! array_key_exists( 'discourse_permalink', $custom ) ) {
|
||||
echo wp_kses_post( Templates::bad_response_html() );
|
||||
|
||||
} else {
|
||||
$options = get_option( 'discourse' );
|
||||
$is_enable_sso = ( isset( $options['enable-sso'] ) && 1 === intval( $options['enable-sso'] ) );
|
||||
$permalink = (string) $custom['discourse_permalink'][0];
|
||||
if ( $is_enable_sso ) {
|
||||
$permalink = esc_url( $options['url'] ) . '/session/sso?return_path=' . $permalink;
|
||||
}
|
||||
$discourse_url_name = preg_replace( '(https?://)', '', esc_url( $options['url'] ) );
|
||||
if ( isset( $custom['discourse_comments_raw'] ) ) {
|
||||
$discourse_info = json_decode( $custom['discourse_comments_raw'][0] );
|
||||
} else {
|
||||
$discourse_info = array();
|
||||
}
|
||||
$defaults = array(
|
||||
'posts_count' => 0,
|
||||
'posts' => array(),
|
||||
'participants' => array(),
|
||||
);
|
||||
|
||||
// Add <time> tag to WP allowed html tags.
|
||||
global $allowedposttags;
|
||||
$allowedposttags['time'] = array( 'datetime' => array() );
|
||||
|
||||
// Use custom datetime format string if provided, else global date format.
|
||||
$datetime_format = '' === $options['custom-datetime-format'] ? get_option( 'date_format' ) : $options['custom-datetime-format'];
|
||||
|
||||
// Add some protection in the event our metadata doesn't look how we expect it to.
|
||||
$discourse_info = (object) wp_parse_args( (array) $discourse_info, $defaults );
|
||||
|
||||
$more_replies = intval( ( $discourse_info->posts_count - count( $discourse_info->posts ) - 1 ) );
|
||||
$more = ( 0 === count( $discourse_info->posts ) ) ? '' : 'more ';
|
||||
|
||||
if ( 0 === $more_replies ) {
|
||||
$more_replies = '';
|
||||
} elseif ( 1 === $more_replies ) {
|
||||
$more_replies = '1 ' . $more . 'reply';
|
||||
} else {
|
||||
$more_replies = $more_replies . ' ' . $more . 'replies';
|
||||
}
|
||||
|
||||
$discourse_url = esc_url( $options['url'] );
|
||||
$discourse_html = '';
|
||||
$comments_html = '';
|
||||
$participants_html = '';
|
||||
|
||||
if ( count( $discourse_info->posts ) > 0 ) {
|
||||
foreach ( $discourse_info->posts as &$post ) {
|
||||
$comment_html = wp_kses_post( Templates::comment_html() );
|
||||
$comment_html = str_replace( '{discourse_url}', $discourse_url, $comment_html );
|
||||
$comment_html = str_replace( '{discourse_url_name}', $discourse_url_name, $comment_html );
|
||||
$comment_html = str_replace( '{topic_url}', $permalink, $comment_html );
|
||||
$avatar_url = DiscourseUtilities::avatar( $post->avatar_template, 64 );
|
||||
$comment_html = str_replace( '{avatar_url}', esc_url( $avatar_url ), $comment_html );
|
||||
$user_url = DiscourseUtilities::homepage( $options['url'], $post );
|
||||
$comment_html = str_replace( '{user_url}', esc_url( $user_url ), $comment_html );
|
||||
$comment_html = str_replace( '{username}', esc_html( $post->username ), $comment_html );
|
||||
$comment_html = str_replace( '{fullname}', esc_html( $post->name ), $comment_html );
|
||||
$comment_body = DiscourseUtilities::convert_relative_img_src_to_absolute( $discourse_url, $post->cooked );
|
||||
$comment_html = str_replace( '{comment_body}', wp_kses_post( $comment_body ), $comment_html );
|
||||
$comment_html = str_replace( '{comment_created_at}', mysql2date( $datetime_format, get_date_from_gmt( $post->created_at ) ), $comment_html );
|
||||
$comments_html .= $comment_html;
|
||||
}
|
||||
foreach ( $discourse_info->participants as &$participant ) {
|
||||
$participant_html = wp_kses_post( Templates::participant_html() );
|
||||
$participant_html = str_replace( '{discourse_url}', $discourse_url, $participant_html );
|
||||
$participant_html = str_replace( '{discourse_url_name}', $discourse_url_name, $participant_html );
|
||||
$participant_html = str_replace( '{topic_url}', $permalink, $participant_html );
|
||||
$avatar_url = DiscourseUtilities::avatar( $participant->avatar_template, 64 );
|
||||
$participant_html = str_replace( '{avatar_url}', esc_url( $avatar_url ), $participant_html );
|
||||
$user_url = DiscourseUtilities::homepage( $options['url'], $participant );
|
||||
$participant_html = str_replace( '{user_url}', esc_url( $user_url ), $participant_html );
|
||||
$participant_html = str_replace( '{username}', esc_html( $participant->username ), $participant_html );
|
||||
$participants_html .= $participant_html;
|
||||
}
|
||||
$discourse_html = wp_kses_post( Templates::replies_html() );
|
||||
$discourse_html = str_replace( '{more_replies}', $more_replies, $discourse_html );
|
||||
} else {
|
||||
$discourse_html = wp_kses_post( Templates::no_replies_html() );
|
||||
}
|
||||
$discourse_html = str_replace( '{discourse_url}', $discourse_url, $discourse_html );
|
||||
$discourse_html = str_replace( '{discourse_url_name}', $discourse_url_name, $discourse_html );
|
||||
$discourse_html = str_replace( '{topic_url}', $permalink, $discourse_html );
|
||||
$discourse_html = str_replace( '{comments}', $comments_html, $discourse_html );
|
||||
$discourse_html = str_replace( '{participants}', $participants_html, $discourse_html );
|
||||
echo wp_kses_post( $discourse_html );
|
||||
}
|
27
tests/bootstrap.php
Normal file
27
tests/bootstrap.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPUnit bootstrap file
|
||||
*
|
||||
* @package wp-discourse
|
||||
*/
|
||||
|
||||
require_once( dirname( __DIR__) . '/vendor/autoload.php' );
|
||||
|
||||
$_tests_dir = getenv( 'WP_TESTS_DIR' );
|
||||
if ( ! $_tests_dir ) {
|
||||
$_tests_dir = '/tmp/wordpress-tests-lib';
|
||||
}
|
||||
|
||||
// Give access to tests_add_filter() function.
|
||||
require_once $_tests_dir . '/includes/functions.php';
|
||||
|
||||
/**
|
||||
* Manually load the plugin being tested.
|
||||
*/
|
||||
function _manually_load_plugin() {
|
||||
require dirname( dirname( __FILE__ ) ) . '/wp-discourse.php';
|
||||
}
|
||||
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
|
||||
|
||||
// Start up the WP testing environment.
|
||||
require $_tests_dir . '/includes/bootstrap.php';
|
18
tests/lib/test-admin.php
Normal file
18
tests/lib/test-admin.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/admin.php' );
|
||||
|
||||
class TestAdmin extends WP_UnitTestCase {
|
||||
protected $admin;
|
||||
|
||||
public function setUp() {
|
||||
$this->admin = new \WPDiscourse\DiscourseAdmin\DiscourseAdmin();
|
||||
}
|
||||
|
||||
public function test_constructor_hooks_into_correct_filters_and_actions() {
|
||||
$this->assertEquals( 10, has_action( 'admin_init', array( $this->admin, 'admin_init' ) ) );
|
||||
$this->assertEquals( 10, has_action( 'admin_enqueue_scripts', array( $this->admin, 'admin_styles' ) ) );
|
||||
$this->assertEquals( 10, has_action( 'admin_menu', array( $this->admin, 'discourse_admin_menu' ) ) );
|
||||
$this->assertEquals( 10, has_action( 'load-settings_page_discourse', array( $this->admin, 'connection_status_notice' ) ) );
|
||||
}
|
||||
}
|
168
tests/lib/test-discourse-comment.php
Normal file
168
tests/lib/test-discourse-comment.php
Normal file
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/discourse-comment.php' );
|
||||
|
||||
class TestDiscourseComment extends WP_UnitTestCase {
|
||||
public $comment;
|
||||
|
||||
public function setUp() {
|
||||
$this->comment = new \WPDiscourse\DiscourseComment\DiscourseComment();
|
||||
|
||||
$options = array(
|
||||
'url' => 'http://forum.example.com',
|
||||
'use-discourse-comments' => 1,
|
||||
'show-existing-comments' => 0,
|
||||
);
|
||||
|
||||
update_option( 'discourse', $options );
|
||||
}
|
||||
|
||||
public function test_constructor_hooks_into_required_filters_and_actions() {
|
||||
$this->assertEquals( 10, has_filter( 'comments_number', array( $this->comment, 'comments_number' ) ) );
|
||||
$this->assertEquals( 20, has_filter( 'comments_template', array( $this->comment, 'comments_template' ) ) );
|
||||
$this->assertEquals( 10, has_action( 'wp_enqueue_scripts', array(
|
||||
$this->comment,
|
||||
'discourse_comments_js'
|
||||
) ) );
|
||||
}
|
||||
|
||||
public function test_comments_template_syncs_comments_if_post_is_published_to_discourse() {
|
||||
global $post;
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 1 );
|
||||
|
||||
$old = 'http://example.com/wordpress/wp-content/themes/twentysixteen/comments.php';
|
||||
|
||||
$comment_mock = $this->getMock( '\WPDiscourse\DiscourseComment\DiscourseComment', array( 'sync_comments' ) );
|
||||
|
||||
$comment_mock->expects( $this->once() )
|
||||
->method( 'sync_comments' )
|
||||
->with( $post_id );
|
||||
|
||||
$comment_mock->comments_template( $old );
|
||||
}
|
||||
|
||||
public function test_comments_template_does_not_syncs_comments_if_post_is_not_published_to_discourse() {
|
||||
global $post;
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
$old = 'http://example.com/wordpress/wp-content/themes/twentysixteen/comments.php';
|
||||
|
||||
$comment_mock = $this->getMock( '\WPDiscourse\DiscourseComment\DiscourseComment', array( 'sync_comments' ) );
|
||||
|
||||
$comment_mock->expects( $this->never() )
|
||||
->method( 'sync_comments' );
|
||||
|
||||
$comment_mock->comments_template( $old );
|
||||
}
|
||||
|
||||
public function test_comments_template_returns_discourse_template_when_post_published_to_discourse_and_there_are_no_wp_comments() {
|
||||
global $post;
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 1 );
|
||||
|
||||
$old = 'http://example.com/wordpress/wp-content/themes/twentysixteen/comments.php';
|
||||
$comment_mock = $this->getMock( '\WPDiscourse\DiscourseComment\DiscourseComment', array(
|
||||
'sync_comments',
|
||||
) );
|
||||
$template = $comment_mock->comments_template( $old );
|
||||
$regex = '/wp-discourse\/templates\/comments.php$/';
|
||||
|
||||
$this->assertEquals( 1, preg_match( $regex, $template ) );
|
||||
}
|
||||
|
||||
public function test_comments_template_returns_wp_template_when_post_not_published_to_discourse() {
|
||||
global $post;
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
$old = 'http://example.com/wordpress/wp-content/themes/twentysixteen/comments.php';
|
||||
$comment_mock = $this->getMock( '\WPDiscourse\DiscourseComment\DiscourseComment', array(
|
||||
'sync_comments',
|
||||
) );
|
||||
$template = $comment_mock->comments_template( $old );
|
||||
|
||||
$this->assertEquals( $old, $template );
|
||||
|
||||
}
|
||||
|
||||
public function test_comments_template_returns_wp_template_when_show_existing_comments_is_true_and_there_are_comments() {
|
||||
global $post;
|
||||
|
||||
$options = array(
|
||||
'use-discourse-comments' => 1,
|
||||
'show-existing-comments' => 1,
|
||||
'existing-comments-heading' => 'Old comments',
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 1 );
|
||||
|
||||
$comment_id = $this->factory->comment->create( array(
|
||||
'comment_post_ID' => $post_id,
|
||||
) );
|
||||
|
||||
$old = 'http://example.com/wordpress/wp-content/themes/twentysixteen/comments.php';
|
||||
$comment_mock = $this->getMock( '\WPDiscourse\DiscourseComment\DiscourseComment', array(
|
||||
'sync_comments',
|
||||
) );
|
||||
|
||||
$template = $comment_mock->comments_template( $old );
|
||||
|
||||
$this->markTestIncomplete(
|
||||
'There needs to be a way to update the comment count property of the post for this test to work.'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public function test_comments_number_returns_wp_comments_number_if_dicourse_comments_are_not_being_used() {
|
||||
global $post;
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
$this->assertEquals( '3 Replies', $this->comment->comments_number( '3 Replies' ) );
|
||||
}
|
||||
|
||||
public function test_comments_number_returns_discourse_comments_number_when_discourse_comments_are_being_used() {
|
||||
global $post;
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_tite' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 1 );
|
||||
update_post_meta( $post_id, 'discourse_comments_count', 5 );
|
||||
|
||||
$comment_mock = $this->getMock( '\WPDiscourse\DiscourseComment\DiscourseComment', array(
|
||||
'sync_comments',
|
||||
) );
|
||||
|
||||
$this->assertEquals( '5 Replies', $comment_mock->comments_number( '3 Replies' ) );
|
||||
}
|
||||
|
||||
public function test_sync_comments_updates_post_metadata() {
|
||||
$this->markTestIncomplete(
|
||||
'It would be nice to be able to test this.'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
221
tests/lib/test-discourse-publish.php
Normal file
221
tests/lib/test-discourse-publish.php
Normal file
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/discourse-publish.php' );
|
||||
|
||||
class TestDiscoursePublish extends WP_UnitTestCase {
|
||||
protected $publisher;
|
||||
|
||||
public function setUp() {
|
||||
$this->publisher = new \WPDiscourse\DiscoursePublish\DiscoursePublish();
|
||||
$options = array(
|
||||
'publish-category' => '',
|
||||
'publish-username' => 'system',
|
||||
'allowed_post_types' => array( 'post' ),
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
}
|
||||
|
||||
public function test_constructor_hooks_into_correct_actions() {
|
||||
$this->assertEquals( 13, has_action( 'save_post', array( $this->publisher, 'publish_post_after_save' ) ) );
|
||||
$this->assertEquals( 10, has_action( 'transition_post_status', array(
|
||||
$this->publisher,
|
||||
'publish_post_after_transition'
|
||||
) ) );
|
||||
$this->assertEquals( 10, has_action( 'xmlrpc_publish_post', array(
|
||||
$this->publisher,
|
||||
'xmlrpc_publish_post_to_discourse'
|
||||
) ) );
|
||||
}
|
||||
|
||||
public function test_publish_post_after_transition_calls_sync_to_discourse_when_valid_post_type_and_publish_to_discourse_is_set() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 1 );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->once() )
|
||||
->method( 'sync_to_discourse' )
|
||||
->with(
|
||||
$post_id,
|
||||
$post->post_title,
|
||||
$post->post_content
|
||||
);
|
||||
|
||||
$publish_mock->publish_post_after_transition( 'publish', 'pending', $post );
|
||||
}
|
||||
|
||||
public function test_publish_post_after_transition_does_not_call_sync_to_discourse_when_transitioning_to_private() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 1 );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->never() )
|
||||
->method( 'sync_to_discourse' );
|
||||
|
||||
$publish_mock->publish_post_after_transition( 'private', 'publish', $post );
|
||||
}
|
||||
|
||||
public function test_publish_post_after_transition_does_not_call_sync_to_discourse_when_publish_to_discourse_not_set() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->never() )
|
||||
->method( 'sync_to_discourse' );
|
||||
|
||||
$publish_mock->publish_post_after_transition( 'publish', 'publish', $post );
|
||||
}
|
||||
|
||||
public function test_publish_post_after_transition_does_not_call_sync_to_discourse_when_not_valid_post_type() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
'post_type' => 'page',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 1 );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->never() )
|
||||
->method( 'sync_to_discourse' );
|
||||
|
||||
$publish_mock->publish_post_after_transition( 'publish', 'publish', $post );
|
||||
}
|
||||
|
||||
public function test_xmlrpc_publish_post_to_discourse_calls_sync_to_discourse_when_valid_post_type_and_publish_to_discourse_is_set() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
'post_type' => 'page',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
$options = array(
|
||||
'publish-category' => 'uncategorized',
|
||||
'publish-username' => 'system',
|
||||
'allowed_post_types' => array( 'page' ),
|
||||
);
|
||||
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->once() )
|
||||
->method( 'sync_to_discourse' )
|
||||
->with( $post_id, $post->post_title, $post->post_content );
|
||||
|
||||
$publish_mock->xmlrpc_publish_post_to_discourse( $post_id );
|
||||
}
|
||||
|
||||
public function test_xmlrpc_publish_post_to_discourse_sets_publish_to_discourse_metadata() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
|
||||
update_post_meta( $post_id, 'publish_to_discourse', 0 );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
|
||||
$publish_mock->xmlrpc_publish_post_to_discourse( $post_id );
|
||||
|
||||
$this->assertEquals( 1, get_post_meta( $post_id, 'publish_to_discourse', true ) );
|
||||
}
|
||||
|
||||
public function test_xmlrpc_publish_post_to_discourse_does_not_call_sync_to_discourse_for_wrong_post_type() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
'post_type' => 'page',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
$options = array(
|
||||
'publish-category' => 'uncategorized',
|
||||
'publish-username' => 'system',
|
||||
'allowed_post_types' => array( 'post' ),
|
||||
);
|
||||
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->never() )
|
||||
->method( 'sync_to_discourse' );
|
||||
|
||||
$publish_mock->xmlrpc_publish_post_to_discourse( $post_id );
|
||||
}
|
||||
|
||||
public function test_publish_post_after_save_publishes_post_when_post_is_set_to_be_published() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
update_post_meta( $post_id,'publish_to_discourse', 1 );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->once() )
|
||||
->method( 'sync_to_discourse' )
|
||||
->with( $post_id, $post->post_title, $post->post_content );
|
||||
|
||||
$publish_mock->publish_post_after_save( $post_id, $post );
|
||||
}
|
||||
|
||||
public function test_publish_post_after_save_does_not_publish_draft() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
update_post_meta( $post_id,'publish_to_discourse', 1 );
|
||||
|
||||
$post_data = array(
|
||||
'ID' => $post_id,
|
||||
'post_status' => 'draft',
|
||||
);
|
||||
wp_update_post( $post_data );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->never() )
|
||||
->method( 'sync_to_discourse' );
|
||||
|
||||
$publish_mock->publish_post_after_save( $post_id, $post );
|
||||
}
|
||||
|
||||
public function test_publish_post_after_save_does_not_publish_wrong_post_type() {
|
||||
$post_id = $this->factory->post->create( array(
|
||||
'post_title' => 'This is a test',
|
||||
'post_type' => 'page',
|
||||
) );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
update_post_meta( $post_id,'publish_to_discourse', 1 );
|
||||
|
||||
$publish_mock = $this->getMock( '\WPDiscourse\DiscoursePublish\DiscoursePublish',
|
||||
array( 'sync_to_discourse' ) );
|
||||
|
||||
$publish_mock->expects( $this->never() )
|
||||
->method( 'sync_to_discourse' );
|
||||
|
||||
$publish_mock->publish_post_after_save( $post_id, $post );
|
||||
}
|
||||
|
||||
}
|
52
tests/lib/test-discourse-sso.php
Normal file
52
tests/lib/test-discourse-sso.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/discourse-sso.php' );
|
||||
|
||||
|
||||
class TestDiscourseSSO extends WP_UnitTestCase {
|
||||
|
||||
protected $discourse_sso;
|
||||
|
||||
public function setUp() {
|
||||
$this->discourse_sso = new \WPDiscourse\DiscourseSSO\DiscourseSSO();
|
||||
$options = array(
|
||||
'enable-sso' => 1,
|
||||
'url' => 'http://forum.example.com',
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
}
|
||||
|
||||
public function test_constructor_hooks_into_correct_filter_and_actions() {
|
||||
$this->assertEquals( 10, has_filter( 'query_vars', array( $this->discourse_sso, 'sso_add_query_vars' ) ) );
|
||||
$this->assertEquals( 10, has_filter( 'login_url', array( $this->discourse_sso, 'set_login_url' ) ) );
|
||||
$this->assertEquals( 10, has_action( 'parse_query', array( $this->discourse_sso, 'sso_parse_request' ) ) );
|
||||
}
|
||||
|
||||
public function test_set_login_url_returns_wp_login_url_if_login_path_not_set() {
|
||||
$options = array(
|
||||
'login-path' => '',
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$wp_login = 'wp-login.php';
|
||||
$redirect = '/';
|
||||
|
||||
$returned_path = explode( '?', $this->discourse_sso->set_login_url( $wp_login, $redirect ) )[0];
|
||||
|
||||
$this->assertEquals( $wp_login, $returned_path );
|
||||
}
|
||||
|
||||
public function test_set_login_url_returns_supplied_login_path() {
|
||||
$options = array(
|
||||
'login-path' => '/',
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$wp_login = 'wp-login.php';
|
||||
$redirect = '/welcome';
|
||||
|
||||
$returned_path = explode( '?', $this->discourse_sso->set_login_url( $wp_login, $redirect ) )[0];
|
||||
|
||||
$this->assertEquals( get_option( 'discourse' )['login-path'], $returned_path );
|
||||
}
|
||||
}
|
15
tests/lib/test-discourse.php
Normal file
15
tests/lib/test-discourse.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/discourse.php' );
|
||||
|
||||
class TestDiscourse extends WP_UnitTestCase {
|
||||
protected $discourse;
|
||||
|
||||
public function setUp() {
|
||||
$this->discourse = new \WPDiscourse\Discourse\Discourse();
|
||||
}
|
||||
|
||||
public function test_constructor_hooks_into_correct_filters_and_actions() {
|
||||
$this->assertEquals( 10, has_filter( 'user_contactmethods', array( $this->discourse, 'extend_user_profile' ) ) );
|
||||
}
|
||||
}
|
67
tests/lib/test-meta-box.php
Normal file
67
tests/lib/test-meta-box.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/meta-box.php' );
|
||||
|
||||
use phpmock\phpunit\PHPMock;
|
||||
|
||||
class TestMetaBox extends \PHPUnit_Framework_TestCase {
|
||||
use PHPMock;
|
||||
|
||||
protected $metabox;
|
||||
|
||||
public function setUp() {
|
||||
$this->metabox = new \WPDiscourse\MetaBox\MetaBox();
|
||||
$options = array(
|
||||
'allowed_post_types' => array( 'post', 'page' ),
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function test_constructor_adds_correct_actions() {
|
||||
$this->assertEquals( 10, has_action( 'add_meta_boxes', array( $this->metabox, 'add_meta_box' ) ) );
|
||||
$this->assertEquals( 10, has_action( 'save_post', array( $this->metabox, 'save_meta_box' ) ) );
|
||||
}
|
||||
|
||||
public function test_add_meta_box_does_not_add_box_to_wrong_post_type() {
|
||||
$add_meta_box = $this->getFunctionMock( 'WPDiscourse\MetaBox', 'add_meta_box' );
|
||||
$add_meta_box->expects( $this->never() );
|
||||
|
||||
$this->metabox->add_meta_box( 'product' );
|
||||
}
|
||||
|
||||
public function test_add_meta_box_adds_meta_box_to_correct_post_type() {
|
||||
$add_meta_box = $this->getFunctionMock( 'WPDiscourse\MetaBox', 'add_meta_box' );
|
||||
$add_meta_box->expects( $this->once() );
|
||||
|
||||
$this->metabox->add_meta_box( 'page' );
|
||||
}
|
||||
|
||||
public function test_save_meta_box_updates_post_has_been_saved_meta_data() {
|
||||
$postarr = array(
|
||||
'ID' => 0,
|
||||
'post_author' => 1,
|
||||
'post_status' => 'publish',
|
||||
'post_title' => 'This is a test',
|
||||
);
|
||||
|
||||
$post_id = wp_insert_post( $postarr, true );
|
||||
|
||||
$_POST['publish_to_discourse_nonce'] = 'nonce';
|
||||
|
||||
$wp_verify_nonce = $this->getFunctionMock( 'WPDiscourse\MetaBox', 'wp_verify_nonce' );
|
||||
$wp_verify_nonce->expects( $this->once() )
|
||||
->with( $this->anything() )
|
||||
->willReturn( true );
|
||||
|
||||
$current_user_can = $this->getFunctionMock( 'WPDiscourse\MetaBox', 'current_user_can' );
|
||||
$current_user_can->expects( $this->once() )
|
||||
->with( $this->anything() )
|
||||
->willReturn( true );
|
||||
|
||||
$this->metabox->save_meta_box( $post_id );
|
||||
|
||||
$this->assertEquals( 1, get_post_meta( $post_id, 'has_been_saved', true ) );
|
||||
}
|
||||
}
|
165
tests/lib/test-settings-validator.php
Normal file
165
tests/lib/test-settings-validator.php
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/settings-validator.php' );
|
||||
|
||||
class TestSettingsValidator extends WP_UnitTestCase {
|
||||
function setUp() {
|
||||
$this->validator = new \WPDiscourse\Validator\SettingsValidator();
|
||||
}
|
||||
|
||||
// Validate URL.
|
||||
|
||||
function test_validate_url_returns_empty_string_for_invalid_protocol() {
|
||||
$urls = array( 'htxtp://example.com', 'example.com', 'mailto://example.com' );
|
||||
|
||||
foreach ( $urls as $url ) {
|
||||
$this->assertEquals( '', $this->validator->validate_url( $url ) );
|
||||
}
|
||||
}
|
||||
|
||||
function test_validate_url_adds_settings_error_for_invalid_input() {
|
||||
$urls = array( 'htxtp://example.com', 'http://', 'http://%xample.com' );
|
||||
|
||||
foreach ( $urls as $url ) {
|
||||
$num_errors = count( get_settings_errors() );
|
||||
$this->validator->validate_url( $url );
|
||||
$new_errors = count( get_settings_errors() );
|
||||
$this->assertNotSame( $num_errors, $new_errors );
|
||||
}
|
||||
}
|
||||
|
||||
function test_validate_url_returns_a_valid_url() {
|
||||
$urls = array( 'http://example.com', 'https://example.com' );
|
||||
|
||||
foreach ( $urls as $url ) {
|
||||
$this->assertSame( $url, $this->validator->validate_url( $url ) );
|
||||
}
|
||||
}
|
||||
|
||||
function test_validate_url_strips_trailing_slash() {
|
||||
$url = 'https://example.com/';
|
||||
|
||||
$this->assertSame( 'https://example.com', $this->validator->validate_url( $url ) );
|
||||
}
|
||||
|
||||
// Validate api key.
|
||||
|
||||
function test_validate_api_key_sanitizes_input() {
|
||||
$api_key = 'thisisatest<script>alert("thisisatest");</script>';
|
||||
|
||||
$this->assertSame( 'thisisatest', $this->validator->validate_api_key( $api_key ) );
|
||||
}
|
||||
|
||||
function test_validate_api_key_adds_settings_error_for_invalid_input() {
|
||||
$api_key = 'this-is-a-test';
|
||||
$num_errors = count( get_settings_errors() );
|
||||
|
||||
$this->validator->validate_api_key( $api_key );
|
||||
|
||||
$new_errors = count( get_settings_errors() );
|
||||
|
||||
$this->assertNotSame( $num_errors, $new_errors );
|
||||
}
|
||||
|
||||
function test_validate_api_key_trims_valid_input() {
|
||||
$api_key = '1wdlkjsoiuelkj3r45 ';
|
||||
|
||||
$this->assertSame( '1wdlkjsoiuelkj3r45', $this->validator->validate_api_key( $api_key ) );
|
||||
}
|
||||
|
||||
// Validate publish category
|
||||
|
||||
function test_validate_publish_category_returns_an_int() {
|
||||
$category = '1';
|
||||
|
||||
$this->assertSame( 1, $this->validator->validate_publish_category( $category ) );
|
||||
}
|
||||
|
||||
// Validate publish category update.
|
||||
|
||||
function test_validate_publish_category_update_returns_1_for_valid() {
|
||||
$update = '1';
|
||||
|
||||
$this->assertSame( 1, $this->validator->validate_publish_category_update( $update ) );
|
||||
}
|
||||
|
||||
function test_validate_publish_category_update_returns_0_for_invalid() {
|
||||
$update = 'update';
|
||||
|
||||
$this->assertSame( 0, $this->validator->validate_publish_category_update( $update ) );
|
||||
}
|
||||
|
||||
// Validate max comments.
|
||||
|
||||
function test_validate_max_comments_adds_settings_error_for_negative_numbers_when_use_discourse_comments_is_true() {
|
||||
$num_errors = count( get_settings_errors() );
|
||||
|
||||
$this->validator->validate_use_discourse_comments( 1 );
|
||||
$this->validator->validate_max_comments( - 100 );
|
||||
|
||||
$new_errors = count( get_settings_errors() );
|
||||
|
||||
|
||||
$this->assertNotSame( $num_errors, $new_errors );
|
||||
}
|
||||
|
||||
function test_validate_max_comments_sanitizes_input_but_does_not_add_error_when_discourse_comments_not_true() {
|
||||
$num_errors = count( get_settings_errors() );
|
||||
|
||||
$this->assertSame( $this->validator->validate_max_comments( 'one hundred' ), '' );
|
||||
|
||||
$new_errors = count( get_settings_errors() );
|
||||
|
||||
$this->assertSame( $num_errors, $new_errors );
|
||||
}
|
||||
|
||||
// Validate sso secret.
|
||||
|
||||
function test_validate_sso_secret_sanitizes_input_but_does_not_add_error_when_not_using_sso() {
|
||||
$num_errors = count( get_settings_errors() );
|
||||
|
||||
$this->assertSame( $this->validator->validate_sso_secret( 'abcde<script>fg</script>' ), 'abcde' );
|
||||
|
||||
$new_errors = count( get_settings_errors() );
|
||||
|
||||
$this->assertSame( $num_errors, $new_errors );
|
||||
}
|
||||
|
||||
function test_validate_sso_secret_adds_settings_error_when_sso_enabled() {
|
||||
$num_errors = count( get_settings_errors() );
|
||||
|
||||
$this->validator->validate_enable_sso( 1 );
|
||||
|
||||
$this->validator->validate_sso_secret( 'abc' );
|
||||
|
||||
$new_errors = count( get_settings_errors() );
|
||||
|
||||
$this->assertNotSame( $num_errors, $new_errors );
|
||||
}
|
||||
|
||||
// Validate login path.
|
||||
|
||||
function test_validate_login_path_must_begin_with_forward_slash() {
|
||||
$this->validator->validate_enable_sso( 1 );
|
||||
|
||||
$num_errors = count( get_settings_errors() );
|
||||
|
||||
$this->validator->validate_login_path( 'my-account' );
|
||||
|
||||
$new_errors = count( get_settings_errors() );
|
||||
|
||||
$this->assertNotSame( $num_errors, $new_errors );
|
||||
}
|
||||
|
||||
function test_validate_login_path_accepts_valid_input() {
|
||||
$paths = array( '/login', '/login/path/', '/login-path' );
|
||||
$this->validator->validate_enable_sso( 1 );
|
||||
|
||||
foreach ( $paths as $path ) {
|
||||
$num_errors = count( get_settings_errors() );
|
||||
$this->validator->validate_login_path( $path );
|
||||
$new_errors = count( get_settings_errors() );
|
||||
$this->assertSame( $num_errors, $new_errors );
|
||||
}
|
||||
}
|
||||
}
|
251
tests/lib/test-utilities.php
Normal file
251
tests/lib/test-utilities.php
Normal file
|
@ -0,0 +1,251 @@
|
|||
<?php
|
||||
|
||||
require_once( __DIR__ . '/../../lib/utilities.php' );
|
||||
use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
|
||||
use phpmock\phpunit\PHPMock;
|
||||
|
||||
class TestUtilities extends \PHPUnit_Framework_TestCase {
|
||||
use PHPMock;
|
||||
|
||||
function test_avatar_substitutes_size_into_template() {
|
||||
$template = 'http://forum.example.com/user_avatar/scossar/{size}/1_1.png';
|
||||
|
||||
$this->assertEquals( 'http://forum.example.com/user_avatar/scossar/60/1_1.png',
|
||||
DiscourseUtilities::avatar( $template, 60 ) );
|
||||
}
|
||||
|
||||
function test_homepage_returns_discourse_users_url() {
|
||||
$post = new \stdClass();
|
||||
$post->username = 'scossar';
|
||||
|
||||
$this->assertEquals( 'http://forum.example.com/users/scossar',
|
||||
DiscourseUtilities::homepage( 'http://forum.example.com', $post ) );
|
||||
}
|
||||
|
||||
function test_convert_relative_img_src_to_absolute_when_supplied_with_absolute_src() {
|
||||
$content = '<img src="http://example.com/uploads/example.png" />';
|
||||
|
||||
$this->assertEquals( $content, DiscourseUtilities::convert_relative_img_src_to_absolute( 'http://example.com', $content ) );
|
||||
}
|
||||
|
||||
function test_convert_relative_img_src_to_absolute_when_first_image_is_absolute_and_second_is_relative() {
|
||||
$this->markTestIncomplete( 'This will fail, fix!' );
|
||||
|
||||
$content = '<img src="//testbucket.s3-us-west-2.amazonaws.com/example.jpg"><img src="/uploads/example.png" />';
|
||||
$expected_result = '<img src="//testbucket.s3-us-west-2.amazonaws.com/example.jpg"><img src="http://example.com/uploads/example.png" />';
|
||||
|
||||
$this->assertEquals( $expected_result, DiscourseUtilities::convert_relative_img_src_to_absolute( 'http://example.com', $content ) );
|
||||
}
|
||||
|
||||
function test_convert_relative_img_src_to_absolute_when_supplied_with_relative_src() {
|
||||
$content = '<img src="/uploads/example.png" />';
|
||||
|
||||
$this->assertEquals( '<img src="http://example.com/uploads/example.png" />', DiscourseUtilities::convert_relative_img_src_to_absolute( 'http://example.com', $content ) );
|
||||
}
|
||||
|
||||
// Brittle test.
|
||||
function test_get_discourse_categories_returns_cached_categories_when_force_update_false_and_there_are_cached_categories() {
|
||||
$this->markTestIncomplete();
|
||||
|
||||
$options = array(
|
||||
'api-key' => 'thisisatest',
|
||||
'publish-username' => 'system',
|
||||
'url' => 'http://forum.example.com',
|
||||
'publish-category-update' => 0,
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$categories = array(
|
||||
'category one',
|
||||
'category two',
|
||||
'category three',
|
||||
);
|
||||
|
||||
$get_transient = $this->getFunctionMock( 'WPDiscourse\Utilities', 'get_transient' );
|
||||
$get_transient->expects( $this->once() )
|
||||
->with( 'discourse_settings_categories_cache' )
|
||||
->willReturn( $categories );
|
||||
|
||||
$this->assertEquals( $categories, DiscourseUtilities::get_discourse_categories() );
|
||||
}
|
||||
|
||||
// Brittle test.
|
||||
function test_discourse_categories_returns_cached_categories_when_remote_returns_an_error() {
|
||||
$this->markTestIncomplete();
|
||||
|
||||
$options = array(
|
||||
'api-key' => 'thisisatest',
|
||||
'publish-username' => 'system',
|
||||
'url' => 'http://forum.example.com',
|
||||
'publish-category-update' => 1,
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$categories = array(
|
||||
'category one',
|
||||
'category two',
|
||||
'category three',
|
||||
);
|
||||
|
||||
$get_transient = $this->getFunctionMock( 'WPDiscourse\Utilities', 'get_transient' );
|
||||
$get_transient->expects( $this->once() )
|
||||
->with( 'discourse_settings_categories_cache' )
|
||||
->willReturn( $categories );
|
||||
|
||||
$wp_remote_get = $this->getFunctionMock( 'WPDiscourse\Utilities', 'wp_remote_get' );
|
||||
$wp_remote_get->expects( $this->once() )
|
||||
->with( $this->anything() )
|
||||
->willReturn( new \WP_Error );
|
||||
|
||||
$this->assertEquals( $categories, DiscourseUtilities::get_discourse_categories() );
|
||||
}
|
||||
|
||||
// Brittle test.
|
||||
public function test_get_discourse_categories_sets_transient() {
|
||||
$this->markTestIncomplete();
|
||||
|
||||
$options = array(
|
||||
'api-key' => 'thisisatest',
|
||||
'publish-username' => 'system',
|
||||
'url' => 'http://forum.example.com',
|
||||
'publish-category-update' => 1,
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$response = array(
|
||||
'categories' => array(
|
||||
array(
|
||||
'id' => 1,
|
||||
'name' => 'category one',
|
||||
),
|
||||
array(
|
||||
'id' => 2,
|
||||
'name' => 'category two',
|
||||
),
|
||||
array(
|
||||
'id' => 3,
|
||||
'name' => 'category three',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Mocks getting the site.json from Discourse.
|
||||
$wp_remote_get = $this->getFunctionMock( 'WPDiscourse\Utilities', 'wp_remote_get' );
|
||||
$wp_remote_get->expects( $this->once() )
|
||||
->with( $this->anything() );
|
||||
|
||||
// Mocks passing validation.
|
||||
$wp_remote_retrieve_response_code = $this->getFunctionMock( 'WPDiscourse\Utilities', 'wp_remote_retrieve_response_code' );
|
||||
$wp_remote_retrieve_response_code->expects( $this->any() )
|
||||
->with( $this->anything() )
|
||||
->willReturn( 200 );
|
||||
|
||||
// Mocks retrieving the body.
|
||||
$wp_remote_retrieve_body = $this->getFunctionMock( 'WPDiscourse\Utilities', 'wp_remote_retrieve_body' );
|
||||
$wp_remote_retrieve_body->expects( $this->any() )
|
||||
->with( $this->anything() );
|
||||
|
||||
// Mocks json_decode. Returns the $response array created above. What is being
|
||||
// tested runs starts here.
|
||||
$json_decode = $this->getFunctionMock( 'WPDiscourse\Utilities', 'json_decode' );
|
||||
$json_decode->expects( $this->once() )
|
||||
->with( $this->anything(), true )
|
||||
->willReturn( $response );
|
||||
|
||||
$set_transient = $this->getFunctionMock( 'WPDiscourse\Utilities', 'set_transient' );
|
||||
$set_transient->expects( $this->once() )
|
||||
->with(
|
||||
'discourse_settings_categories_cache',
|
||||
$response['categories'],
|
||||
HOUR_IN_SECONDS
|
||||
);
|
||||
|
||||
DiscourseUtilities::get_discourse_categories();
|
||||
}
|
||||
|
||||
// Brittle test.
|
||||
public function test_get_discourse_categories_removes_subcategories_when_display_subcategories_is_not_set() {
|
||||
$this->markTestIncomplete();
|
||||
|
||||
$options = array(
|
||||
'api-key' => 'thisisatest',
|
||||
'publish-username' => 'system',
|
||||
'url' => 'http://forum.example.com',
|
||||
'publish-category-update' => 1,
|
||||
'display-subcategories' => 0
|
||||
);
|
||||
update_option( 'discourse', $options );
|
||||
|
||||
$response = array(
|
||||
'categories' => array(
|
||||
array(
|
||||
'id' => 1,
|
||||
'name' => 'category one',
|
||||
),
|
||||
array(
|
||||
'id' => 2,
|
||||
'name' => 'category two',
|
||||
'parent_category_id' => 1,
|
||||
),
|
||||
array(
|
||||
'id' => 3,
|
||||
'name' => 'category three',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Mocks getting the site.json from Discourse.
|
||||
$wp_remote_get = $this->getFunctionMock( 'WPDiscourse\Utilities', 'wp_remote_get' );
|
||||
$wp_remote_get->expects( $this->once() )
|
||||
->with( $this->anything() );
|
||||
|
||||
// Mocks passing validation.
|
||||
$wp_remote_retrieve_response_code = $this->getFunctionMock( 'WPDiscourse\Utilities', 'wp_remote_retrieve_response_code' );
|
||||
$wp_remote_retrieve_response_code->expects( $this->any() )
|
||||
->with( $this->anything() )
|
||||
->willReturn( 200 );
|
||||
|
||||
// Mocks retrieving the body.
|
||||
$wp_remote_retrieve_body = $this->getFunctionMock( 'WPDiscourse\Utilities', 'wp_remote_retrieve_body' );
|
||||
$wp_remote_retrieve_body->expects( $this->any() )
|
||||
->with( $this->anything() );
|
||||
|
||||
// Mocks json_decode. Returns the $response array created above. What is being
|
||||
// tested runs starts here.
|
||||
$json_decode = $this->getFunctionMock( 'WPDiscourse\Utilities', 'json_decode' );
|
||||
$json_decode->expects( $this->once() )
|
||||
->with( $this->anything(), true )
|
||||
->willReturn( $response );
|
||||
|
||||
// The subcategory is removed.
|
||||
$this->assertEquals( 2, count( DiscourseUtilities::get_discourse_categories() ) );
|
||||
}
|
||||
|
||||
public function test_validate_returns_zero_for_a_wp_error() {
|
||||
$response = new \WP_Error();
|
||||
$this->assertEquals( 0, DiscourseUtilities::validate( $response ) );
|
||||
}
|
||||
|
||||
public function test_validate_returns_zero_if_response_code_is_not_200() {
|
||||
$response_codes = array( 300, 400, 500 );
|
||||
foreach ( $response_codes as $code ) {
|
||||
$response = array(
|
||||
'response' => array(
|
||||
'code' => $code,
|
||||
'message' => 'not found',
|
||||
),
|
||||
);
|
||||
$this->assertEquals( 0, DiscourseUtilities::validate( $response ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function test_validate_returns_one_if_response_code_is_200() {
|
||||
$response = array(
|
||||
'response' => array(
|
||||
'code' => 200,
|
||||
'message' => 'not found',
|
||||
),
|
||||
);
|
||||
$this->assertEquals( 1, DiscourseUtilities::validate( $response ) );
|
||||
}
|
||||
}
|
14
uninstall.php
Normal file
14
uninstall.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* Uninstall the plugin.
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
delete_option( 'discourse_version' );
|
||||
delete_option( 'discourse' );
|
||||
delete_transient( 'discourse_settings_categories_cache' );
|
436
wp-discourse.php
436
wp-discourse.php
|
@ -1,381 +1,57 @@
|
|||
<?php
|
||||
/*
|
||||
Plugin Name: WP-Discourse
|
||||
Description: Allows you to publish your posts to a Discourse instance and view top Discourse comments on your blog
|
||||
Version: 0.0.1
|
||||
Author: Sam Saffron
|
||||
Author URI: http://www.discourse.org
|
||||
*/
|
||||
/* Copyright 2011 Sam Saffron (sam.saffron@discourse.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
class Discourse {
|
||||
|
||||
var $domain = 'discourse';
|
||||
|
||||
//Version
|
||||
static $version ='0.0.1';
|
||||
|
||||
//Options and defaults
|
||||
static $options = array(
|
||||
'url'=>'',
|
||||
'api-key'=>'',
|
||||
'publish-username'=>'',
|
||||
'publish-category'=>'',
|
||||
'auto-publish'=>0,
|
||||
'auto-update'=>0,
|
||||
'max-comment'=>5,
|
||||
'use-discourse-comments'=>0,
|
||||
'use-fullname-in-comments'=>1,
|
||||
'publish-format'=>'<small>Originally published at: {blogurl}</small><br>{excerpt}'
|
||||
);
|
||||
|
||||
public function __construct() {
|
||||
register_activation_hook(__FILE__,array(__CLASS__, 'install' ));
|
||||
register_uninstall_hook(__FILE__,array( __CLASS__, 'uninstall' ));
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'admin_menu', array( $this, 'discourse_admin_menu' ));
|
||||
}
|
||||
|
||||
static function install(){
|
||||
update_option("discourse_version",self::$version);
|
||||
add_option('discourse',self::$options);
|
||||
}
|
||||
|
||||
static function uninstall(){
|
||||
delete_option('discourse_version');
|
||||
delete_option('discourse');
|
||||
}
|
||||
|
||||
|
||||
public function init() {
|
||||
//Allow translations
|
||||
load_plugin_textdomain( 'discourse', false, basename(dirname(__FILE__)).'/languages');
|
||||
|
||||
//replace comments with discourse comments
|
||||
|
||||
add_filter('comments_number', array($this,'comments_number'));
|
||||
add_filter('comments_template', array($this,'comments_template'));
|
||||
|
||||
}
|
||||
|
||||
function comments_number($count) {
|
||||
|
||||
|
||||
global $post;
|
||||
if(self::use_discourse_comments($post->ID)){
|
||||
self::sync_comments($post->ID);
|
||||
$count = get_post_meta($post->ID, 'discourse_comments_count', true);
|
||||
if(!$count){
|
||||
$count = "Leave a reply";
|
||||
} else {
|
||||
$count = $count == 1 ? "1 Reply" : $count . " Replies";
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
function use_discourse_comments($postid){
|
||||
return get_post_meta($postid, 'publish_to_discourse', true) == 1 &&
|
||||
get_post_meta($postid, 'discourse_post_id', true) > 0;
|
||||
}
|
||||
|
||||
function sync_comments($postid) {
|
||||
global $wpdb;
|
||||
|
||||
# every 10 minutes do a json call to sync comment count and top comments
|
||||
$last_sync = (int)get_post_meta($postid, 'discourse_last_sync', true);
|
||||
$time = date_timestamp_get(date_create());
|
||||
if($last_sync + 60 * 10 < $time) {
|
||||
|
||||
$got_lock = $wpdb->get_row( "SELECT GET_LOCK('discourse_lock', 0) got_it");
|
||||
if($got_lock->got_it == "1") {
|
||||
|
||||
$discourse_options = get_option('discourse');
|
||||
$comment_count = intval($discourse_options['max-comments']);
|
||||
$permalink = (string)get_post_meta($postid, 'discourse_permalink', true) . '.json?best=' . $comment_count;
|
||||
$soptions = array('http' => array('ignore_errors' => true, 'method' => 'GET'));
|
||||
$context = stream_context_create($soptions);
|
||||
$result = file_get_contents($permalink, false, $context);
|
||||
$json = json_decode($result);
|
||||
|
||||
delete_post_meta($postid, 'discourse_comments_count');
|
||||
add_post_meta($postid, 'discourse_comments_count', $json->posts_count - 1 , true);
|
||||
|
||||
delete_post_meta($postid, 'discourse_comments_raw');
|
||||
|
||||
add_post_meta($postid, 'discourse_comments_raw', $wpdb->escape($result) , true);
|
||||
|
||||
delete_post_meta($postid, 'discourse_last_sync');
|
||||
add_post_meta($postid, 'discourse_last_sync', $time, true);
|
||||
$wpdb->get_results("SELECT RELEASE_LOCK('discourse_lock')");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function comments_template($old) {
|
||||
global $post;
|
||||
|
||||
if(self::use_discourse_comments($post->ID)) {
|
||||
self::sync_comments($post->ID);
|
||||
return dirname(__FILE__) . '/comments.php';
|
||||
}
|
||||
|
||||
return $old;
|
||||
}
|
||||
|
||||
/*
|
||||
* Settings
|
||||
*/
|
||||
public function admin_init(){
|
||||
register_setting( 'discourse', 'discourse', array($this, 'discourse_validate_options'));
|
||||
add_settings_section( 'default_discourse', 'Default Settings', array($this, 'init_default_settings'), 'discourse' );
|
||||
add_settings_field('discourse_url', 'Url', array($this, 'url_input'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_api_key', 'API Key', array($this, 'api_key_input'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_publish_username', 'Publishing username', array($this, 'publish_username_input'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_publish_category', 'Published category', array($this, 'publish_category_input'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_publish_format', 'Publish format', array($this, 'publish_format_textarea'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_auto_publish', 'Auto Publish', array($this, 'auto_publish_checkbox'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_auto_update', 'Auto Update Posts', array($this, 'auto_update_checkbox'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_use_discourse_comments', 'Use Discourse Comments', array($this, 'use_discourse_comments_checkbox'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_max_comments', 'Max visible comments', array($this, 'max_comments_input'), 'discourse', 'default_discourse');
|
||||
add_settings_field('discourse_use_fullname_in_comments', 'Full name in comments', array($this, 'use_fullname_in_comments_checkbox'), 'discourse', 'default_discourse');
|
||||
|
||||
|
||||
add_action( 'post_submitbox_misc_actions', array($this,'publish_to_discourse'));
|
||||
add_action( 'save_post', array($this, 'save_postdata'));
|
||||
add_action ( 'transition_post_status', array($this, 'post_status_changed'), 10, 3 );
|
||||
}
|
||||
|
||||
function post_status_changed($old, $new, $post){
|
||||
if($post->post_type == "revision") { return; }
|
||||
if($new == 'publish' && get_post_meta($post->ID, 'publish_to_discourse', true) == 1) {
|
||||
self::sync_to_discourse($post->ID, $post->post_title, $post->post_content);
|
||||
}
|
||||
}
|
||||
|
||||
function save_postdata($postid)
|
||||
{
|
||||
if ( !current_user_can( 'edit_page', $postid ) ) return $postid;
|
||||
if(empty($postid) || !isset($_POST['publish_to_discourse'])) return $postid;
|
||||
|
||||
# trust me ... word press is crazy like this, try changing a title.
|
||||
if(!isset($_POST['ID'])) return $postid;
|
||||
|
||||
if($_POST['action'] == 'editpost'){
|
||||
delete_post_meta($_POST['ID'], 'publish_to_discourse');
|
||||
}
|
||||
|
||||
$publish = $_POST['publish_to_discourse'];
|
||||
add_post_meta($_POST['ID'], 'publish_to_discourse', $publish, true);
|
||||
|
||||
return $postid;
|
||||
}
|
||||
|
||||
function sync_to_discourse($postid, $title, $raw) {
|
||||
$discourse_id = get_post_meta($postid, 'discourse_post_id', true);
|
||||
$options = get_option('discourse');
|
||||
$post = get_post($post_id);
|
||||
|
||||
|
||||
$excerpt = apply_filters('the_content', $raw);
|
||||
$excerpt = wp_trim_words($excerpt);
|
||||
|
||||
$baked = $options['publish-format'];
|
||||
$baked = str_replace("{excerpt}", $excerpt, $baked);
|
||||
$baked = str_replace("{blogurl}", get_permalink($postid), $baked);
|
||||
|
||||
$data = array(
|
||||
'wp-id' => $postid,
|
||||
'api_key' => $options['api-key'],
|
||||
'api_username' => $options['publish-username'],
|
||||
'title' => $title,
|
||||
'post[raw]' => $baked,
|
||||
'post[category]' => $options['publish-category']
|
||||
);
|
||||
|
||||
|
||||
if(!$discourse_id > 0) {
|
||||
$url = $options['url'] .'/posts';
|
||||
|
||||
// use key 'http' even if you send the request to https://...
|
||||
$soptions = array('http' => array('ignore_errors' => true, 'method' => 'POST','content' => http_build_query($data)));
|
||||
$context = stream_context_create($soptions);
|
||||
$result = file_get_contents($url, false, $context);
|
||||
$json = json_decode($result);
|
||||
|
||||
#todo may have $json->errors with list of errors
|
||||
|
||||
if(property_exists($json, 'id')) {
|
||||
$discourse_id = (int)$json->id;
|
||||
}
|
||||
|
||||
|
||||
if(isset($discourse_id) && $discourse_id > 0) {
|
||||
add_post_meta($postid, 'discourse_post_id', $discourse_id, true);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
# for now the updates are just causing grief, leave'em out
|
||||
return;
|
||||
$url = $options['url'] .'/posts/' . $discourse_id ;
|
||||
$soptions = array('http' => array('ignore_errors' => true, 'method' => 'PUT','content' => http_build_query($data)));
|
||||
$context = stream_context_create($soptions);
|
||||
$result = file_get_contents($url, false, $context);
|
||||
$json = json_decode($result);
|
||||
|
||||
if(isset($json->post)) {
|
||||
$json = $json->post;
|
||||
}
|
||||
|
||||
# todo may have $json->errors with list of errors
|
||||
}
|
||||
|
||||
if(isset($json->topic_slug)){
|
||||
delete_post_meta($postid,'discourse_permalink');
|
||||
add_post_meta($postid,'discourse_permalink', $options['url'] . '/t/' . $json->topic_slug . '/' . $json->topic_id, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function publish_to_discourse()
|
||||
{
|
||||
global $post;
|
||||
$value = get_post_meta($post->ID, 'publish_to_discourse', true);
|
||||
echo '<div class="misc-pub-section misc-pub-section-last">
|
||||
<span>'
|
||||
. '<label><input type="checkbox"' . (!empty($value) ? ' checked="checked" ' : null) . 'value="1" name="publish_to_discourse" /> Publish to Discourse</label>'
|
||||
.'</span></div>';
|
||||
}
|
||||
|
||||
|
||||
function init_default_settings() {
|
||||
|
||||
}
|
||||
|
||||
function url_input(){
|
||||
self::text_input('url', 'Enter your discourse url Eg: http://discuss.mysite.com');
|
||||
}
|
||||
|
||||
function api_key_input(){
|
||||
self::text_input('api-key', '');
|
||||
}
|
||||
|
||||
function publish_username_input(){
|
||||
self::text_input('publish-username', 'Discourse username of publisher');
|
||||
}
|
||||
|
||||
function publish_category_input(){
|
||||
self::text_input('publish-category', 'Category post will be published in Discourse (optional)');
|
||||
}
|
||||
|
||||
function publish_format_textarea(){
|
||||
self::text_area('publish-format', 'Markdown format for published articles, use {excerpt} for excerpt and {blogurl} for the url of the blog post');
|
||||
}
|
||||
|
||||
function max_comments_input(){
|
||||
self::text_input('max-comments', 'Maximum number of comments to display');
|
||||
}
|
||||
|
||||
function use_fullname_in_comments_checkbox(){
|
||||
self::checkbox_input('use-fullname-in-comments', 'Use the users full name in blog comment section');
|
||||
}
|
||||
|
||||
function auto_publish_checkbox(){
|
||||
self::checkbox_input('auto-publish', 'Publish all new posts to Discourse');
|
||||
}
|
||||
|
||||
function auto_update_checkbox(){
|
||||
self::checkbox_input('auto-update', 'Update published blog posts on Discourse');
|
||||
}
|
||||
|
||||
function use_discourse_comments_checkbox(){
|
||||
self::checkbox_input('use-discourse-comments', 'Use Discourse to comment on Discourse published posts (hiding existing comment section)');
|
||||
}
|
||||
|
||||
function checkbox_input($option, $description) {
|
||||
|
||||
$options = get_option( 'discourse' );
|
||||
if (array_key_exists($option, $options) and $options[$option] == 1) {
|
||||
$value = 'checked="checked"';
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<input id='discourse_<?php echo $option?>' name='discourse[<?php echo $option?>]' type='checkbox' value='1' <?php echo $value?> /> <?php echo $description ?>
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
function text_input($option, $description) {
|
||||
|
||||
$options = get_option( 'discourse' );
|
||||
if (array_key_exists($option, $options)) {
|
||||
$value = $options[$option];
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
?>
|
||||
<input id='discourse_<?php echo $option?>' name='discourse[<?php echo $option?>]' type='text' value='<?php echo esc_attr( $value ); ?>' /> <?php echo $description ?>
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
function text_area($option, $description) {
|
||||
|
||||
$options = get_option( 'discourse' );
|
||||
if (array_key_exists($option, $options)) {
|
||||
$value = $options[$option];
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
?>
|
||||
<textarea cols=100 rows=6 id='discourse_<?php echo $option?>' name='discourse[<?php echo $option?>]'><?php echo esc_attr( $value ); ?></textarea><br><?php echo $description ?>
|
||||
<?php
|
||||
|
||||
}
|
||||
function discourse_validate_options($input) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
function discourse_admin_menu(){
|
||||
add_options_page( 'Discourse', 'Discourse', 'manage_options', 'discourse', array ( $this, 'discourse_options_page' ));
|
||||
}
|
||||
|
||||
function discourse_options_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h2>Discourse Options</h2>
|
||||
<form action="options.php" method="POST">
|
||||
<?php settings_fields( 'discourse' ); ?>
|
||||
<?php do_settings_sections('discourse'); ?>
|
||||
<?php submit_button(); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$discourse = new Discourse();
|
||||
/**
|
||||
* Plugin Name: WP-Discourse
|
||||
* Description: Use Discourse as a community engine for your WordPress blog
|
||||
* Version: 0.7.0
|
||||
* Author: Sam Saffron, Robin Ward
|
||||
* Text Domain: wp-discourse
|
||||
* Domain Path: /languages
|
||||
* Author URI: https://github.com/discourse/wp-discourse
|
||||
* Plugin URI: https://github.com/discourse/wp-discourse
|
||||
* GitHub Plugin URI: https://github.com/discourse/wp-discourse
|
||||
*
|
||||
* @package WPDiscourse
|
||||
*/
|
||||
|
||||
/** Copyright 2014 Civilized Discourse Construction Kit, Inc (team@discourse.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
define( 'WPDISCOURSE_PATH', plugin_dir_path( __FILE__ ) );
|
||||
define( 'WPDISCOURSE_URL', plugins_url( '', __FILE__ ) );
|
||||
|
||||
require_once( __DIR__ . '/lib/utilities.php' );
|
||||
require_once( __DIR__ . '/lib/html-templates.php' );
|
||||
require_once( __DIR__ . '/lib/discourse.php' );
|
||||
require_once( __DIR__ . '/lib/settings-validator.php' );
|
||||
require_once( __DIR__ . '/lib/admin.php' );
|
||||
require_once( __DIR__ . '/lib/sso.php' );
|
||||
require_once( __DIR__ . '/lib/discourse-sso.php' );
|
||||
require_once( __DIR__ . '/lib/discourse-publish.php' );
|
||||
require_once( __DIR__ . '/lib/discourse-comment.php' );
|
||||
require_once( __DIR__ . '/lib/meta-box.php' );
|
||||
require_once( __DIR__ . '/lib/plugin-support/woocommerce-support.php' );
|
||||
|
||||
$discourse_settings_validator = new WPDiscourse\Validator\SettingsValidator();
|
||||
$discourse = new WPDiscourse\Discourse\Discourse();
|
||||
$discourse_admin = new WPDiscourse\DiscourseAdmin\DiscourseAdmin();
|
||||
$discourse_publisher = new WPDiscourse\DiscoursePublish\DiscoursePublish();
|
||||
$discourse_comment = new WPDiscourse\DiscourseComment\DiscourseComment();
|
||||
$discourse_sso = new WPDiscourse\DiscourseSSO\DiscourseSSO();
|
||||
$discourse_publish_metabox = new WPDiscourse\MetaBox\MetaBox();
|
||||
$discourse_woocommerce = new WPDiscourse\PluginSupport\WooCommerceSupport();
|
||||
|
||||
register_activation_hook( __FILE__, array( $discourse, 'install' ) );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue