Initial commit

This commit is contained in:
Toby Zerner 2015-09-04 12:44:02 +09:30
commit 2762cf5f99
23 changed files with 658 additions and 0 deletions

View File

@ -0,0 +1,19 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.php]
indent_size = 4

View File

@ -0,0 +1,5 @@
**/bower_components/**/*
**/node_modules/**/*
vendor/**/*
**/Gulpfile.js
**/dist/**/*

View File

@ -0,0 +1,175 @@
{
"parser": "babel-eslint", // https://github.com/babel/babel-eslint
"env": { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments
"browser": true // browser global variables
},
"ecmaFeatures": {
"arrowFunctions": true,
"blockBindings": true,
"classes": true,
"defaultParams": true,
"destructuring": true,
"forOf": true,
"generators": false,
"modules": true,
"objectLiteralComputedProperties": true,
"objectLiteralDuplicateProperties": false,
"objectLiteralShorthandMethods": true,
"objectLiteralShorthandProperties": true,
"spread": true,
"superInFunctions": true,
"templateStrings": true,
"jsx": true
},
"globals": {
"m": true,
"app": true,
"$": true,
"moment": true
},
"plugins": [
"react"
],
"rules": {
"react/jsx-uses-vars": 1,
/**
* Strict mode
*/
// babel inserts "use strict"; for us
"strict": [2, "never"], // http://eslint.org/docs/rules/strict
/**
* ES6
*/
"no-var": 2, // http://eslint.org/docs/rules/no-var
"prefer-const": 2, // http://eslint.org/docs/rules/prefer-const
/**
* Variables
*/
"no-shadow": 2, // http://eslint.org/docs/rules/no-shadow
"no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names
"no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars
"vars": "local",
"args": "after-used"
}],
"no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define
/**
* Possible errors
*/
"comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle
"no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign
"no-console": 1, // http://eslint.org/docs/rules/no-console
"no-debugger": 1, // http://eslint.org/docs/rules/no-debugger
"no-alert": 1, // http://eslint.org/docs/rules/no-alert
"no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition
"no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys
"no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case
"no-empty": 2, // http://eslint.org/docs/rules/no-empty
"no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign
"no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast
"no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi
"no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign
"no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations
"no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp
"no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace
"no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls
"no-reserved-keys": 2, // http://eslint.org/docs/rules/no-reserved-keys
"no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays
"no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable
"use-isnan": 2, // http://eslint.org/docs/rules/use-isnan
"block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var
/**
* Best practices
*/
"consistent-return": 2, // http://eslint.org/docs/rules/consistent-return
"curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly
"default-case": 2, // http://eslint.org/docs/rules/default-case
"dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation
"allowKeywords": true
}],
"eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq
"no-caller": 2, // http://eslint.org/docs/rules/no-caller
"no-else-return": 2, // http://eslint.org/docs/rules/no-else-return
"no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null
"no-eval": 2, // http://eslint.org/docs/rules/no-eval
"no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native
"no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind
"no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough
"no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal
"no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval
"no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks
"no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func
"no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str
"no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign
"no-new": 2, // http://eslint.org/docs/rules/no-new
"no-new-func": 2, // http://eslint.org/docs/rules/no-new-func
"no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers
"no-octal": 2, // http://eslint.org/docs/rules/no-octal
"no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape
"no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign
"no-proto": 2, // http://eslint.org/docs/rules/no-proto
"no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare
"no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign
"no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare
"no-sequences": 2, // http://eslint.org/docs/rules/no-sequences
"no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal
"no-with": 2, // http://eslint.org/docs/rules/no-with
"radix": 2, // http://eslint.org/docs/rules/radix
"vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top
"wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife
"yoda": 2, // http://eslint.org/docs/rules/yoda
/**
* Style
*/
"indent": [2, 2], // http://eslint.org/docs/rules/indent
"brace-style": [2, // http://eslint.org/docs/rules/brace-style
"1tbs", {
"allowSingleLine": true
}],
"quotes": [
2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes
],
"camelcase": [2, { // http://eslint.org/docs/rules/camelcase
"properties": "never"
}],
"comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing
"before": false,
"after": true
}],
"comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style
"eol-last": 2, // http://eslint.org/docs/rules/eol-last
"key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing
"beforeColon": false,
"afterColon": true
}],
"new-cap": [2, { // http://eslint.org/docs/rules/new-cap
"newIsCap": true
}],
"no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
"max": 2
}],
"no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
"no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
"no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
"no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func
"no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle
"one-var": [2, "never"], // http://eslint.org/docs/rules/one-var
"padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks
"semi": [2, "always"], // http://eslint.org/docs/rules/semi
"semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing
"before": false,
"after": true
}],
"space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords
"space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks
"space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren
"space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops
"space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case
"spaced-line-comment": 2, // http://eslint.org/docs/rules/spaced-line-comment
}
}

4
extensions/akismet/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/vendor
composer.phar
.DS_Store
Thumbs.db

26
extensions/akismet/.php_cs Executable file
View File

@ -0,0 +1,26 @@
<?php
$header = <<<EOF
This file is part of Flarum.
(c) Toby Zerner <toby.zerner@gmail.com>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
EOF;
Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header);
$finder = Symfony\CS\Finder\DefaultFinder::create()
->exclude('js')
->exclude('less')
->in(__DIR__);
return Symfony\CS\Config\Config::create()
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
->fixers([
'short_array_syntax',
'header_comment',
'-psr0'
])
->finder($finder);

View File

@ -0,0 +1,23 @@
language: php
php:
- 5.5
- 5.6
matrix:
allow_failures:
- php: hhvm
fast_finish: true
before_script:
- curl -s http://getcomposer.org/installer | php
- php composer.phar install
script:
- php composer.phar style
notifications:
email:
on_failure: change
sudo: false

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2015 Toby Zerner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,14 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/vendor/autoload.php';
return 'Flarum\Akismet\Extension';

View File

@ -0,0 +1,13 @@
{
"autoload": {
"psr-4": {
"Flarum\\Akismet\\": "src/"
}
},
"require": {
"tijsverkoyen/akismet": "^1.1"
},
"scripts": {
"style": "phpcs --standard=PSR2 -np src"
}
}

57
extensions/akismet/composer.lock generated Normal file
View File

@ -0,0 +1,57 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "f4f26918432f8f6ed40008ffd516708d",
"packages": [
{
"name": "tijsverkoyen/akismet",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/tijsverkoyen/Akismet.git",
"reference": "0c5185956e11824ed310d134d38d0b2c936f3d3a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tijsverkoyen/Akismet/zipball/0c5185956e11824ed310d134d38d0b2c936f3d3a",
"reference": "0c5185956e11824ed310d134d38d0b2c936f3d3a",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.2.0"
},
"type": "library",
"autoload": {
"classmap": [
""
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD"
],
"authors": [
{
"name": "Tijs Verkoyen",
"email": "akismet@verkoyen.eu",
"role": "Developer"
}
],
"description": "Akismet is a wrapper-class to communicate with the Akismet API.",
"homepage": "https://github.com/tijsverkoyen/Akismet",
"time": "2012-10-13 17:59:41"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

View File

@ -0,0 +1,26 @@
{
"name": "akismet",
"title": "Akismet",
"description": "Stop spam using the Akismet anti-spam service.",
"keywords": [],
"version": "0.1.0-beta.2",
"author": {
"name": "Toby Zerner",
"email": "toby@flarum.org",
"homepage": "http://tobyzerner.com"
},
"license": "MIT",
"require": {
"flarum": ">=0.1.0-beta.2",
"reports": ">=0.1.0-beta.2"
},
"support": {
"source": "https://github.com/flarum/akismet",
"issues": "https://github.com/flarum/core/issues"
},
"icon": {
"name": "filter",
"backgroundColor": "#5e8e14",
"color": "#fff"
}
}

3
extensions/akismet/js/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
bower_components
node_modules
dist

View File

@ -0,0 +1,7 @@
var gulp = require('flarum-gulp');
gulp({
modules: {
'akismet': 'src/**/*.js'
}
});

View File

@ -0,0 +1,7 @@
{
"private": true,
"devDependencies": {
"gulp": "^3.8.11",
"flarum-gulp": "^0.1.0"
}
}

View File

@ -0,0 +1,57 @@
import Modal from 'flarum/components/Modal';
import Button from 'flarum/components/Button';
import saveConfig from 'flarum/utils/saveConfig';
export default class AkismetSettingsModal extends Modal {
constructor(...args) {
super(...args);
this.apiKey = m.prop(app.config['akismet.api_key'] || '');
}
className() {
return 'AkismetSettingsModal Modal--small';
}
title() {
return 'Akismet Settings';
}
content() {
return (
<div className="Modal-body">
<div className="Form">
<div className="Form-group">
<label>API Key</label>
<input className="FormControl" value={this.apiKey()} oninput={m.withAttr('value', this.apiKey)}/>
</div>
<div className="Form-group">
{Button.component({
type: 'submit',
className: 'Button Button--primary AkismetSettingsModal-save',
loading: this.loading,
children: 'Save Changes'
})}
</div>
</div>
</div>
);
}
onsubmit(e) {
e.preventDefault();
this.loading = true;
saveConfig({
'akismet.api_key': this.apiKey()
}).then(
() => this.hide(),
() => {
this.loading = false;
m.redraw();
}
);
}
}

View File

@ -0,0 +1,8 @@
import { extend } from 'flarum/extend';
import app from 'flarum/app';
import AkismetSettingsModal from 'akismet/components/AkismetSettingsModal';
app.initializers.add('akismet', () => {
app.extensionSettings.akismet = () => app.modal.show(new AkismetSettingsModal());
});

View File

@ -0,0 +1,7 @@
var gulp = require('flarum-gulp');
gulp({
modules: {
'akismet': 'src/**/*.js'
}
});

View File

@ -0,0 +1,7 @@
{
"private": true,
"devDependencies": {
"gulp": "^3.8.11",
"flarum-gulp": "^0.1.0"
}
}

View File

@ -0,0 +1,24 @@
import { extend } from 'flarum/extend';
import app from 'flarum/app';
import Button from 'flarum/components/Button';
import CommentPost from 'flarum/components/CommentPost';
import PostControls from 'flarum/utils/PostControls';
app.initializers.add('akismet', () => {
extend(CommentPost.prototype, 'reportActionItems', function(items) {
if (this.props.post.reports()[0].reporter() === 'Akismet') {
items.add('notSpam',
<Button className="Button"
icon="check"
onclick={() => {
this.dismissReport({akismet: 'ham'}).then(
() => PostControls.restoreAction.apply(this.props.post)
);
}}>
Not Spam
</Button>
);
}
});
}, -10); // set initializer priority to run after reports

View File

@ -0,0 +1,2 @@
akismet:
# hello_world: "Hello, world!"

View File

@ -0,0 +1,23 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Akismet;
use Flarum\Support\Extension as BaseExtension;
use Illuminate\Events\Dispatcher;
class Extension extends BaseExtension
{
public function listen(Dispatcher $events)
{
$events->subscribe('Flarum\Akismet\Listeners\AddClientAssets');
$events->subscribe('Flarum\Akismet\Listeners\ValidatePost');
}
}

View File

@ -0,0 +1,52 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Akismet\Listeners;
use Flarum\Events\RegisterLocales;
use Flarum\Events\BuildClientView;
use Illuminate\Contracts\Events\Dispatcher;
class AddClientAssets
{
public function subscribe(Dispatcher $events)
{
$events->listen(RegisterLocales::class, [$this, 'addLocale']);
$events->listen(BuildClientView::class, [$this, 'addAssets']);
}
public function addLocale(RegisterLocales $event)
{
$event->addTranslations('en', __DIR__.'/../../locale/en.yml');
}
public function addAssets(BuildClientView $event)
{
$event->forumAssets([
__DIR__.'/../../js/forum/dist/extension.js'
]);
$event->forumBootstrapper('akismet/main');
$event->forumTranslations([
// 'akismet.hello_world'
]);
$event->adminAssets([
__DIR__.'/../../js/admin/dist/extension.js'
]);
$event->adminBootstrapper('akismet/main');
$event->adminTranslations([
// 'akismet.hello_world'
]);
}
}

View File

@ -0,0 +1,78 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Akismet\Listeners;
use Flarum\Events\PostWillBeSaved;
use Illuminate\Contracts\Events\Dispatcher;
use TijsVerkoyen\Akismet\Akismet;
use Flarum\Core;
use Flarum\Core\Posts\CommentPost;
use Flarum\Core\Settings\SettingsRepository;
use Flarum\Report\Report;
class ValidatePost
{
protected $settings;
private $savingPost;
public function __construct(SettingsRepository $settings)
{
$this->settings = $settings;
}
public function subscribe(Dispatcher $events)
{
$events->listen(PostWillBeSaved::class, [$this, 'validatePost']);
}
public function validatePost(PostWillBeSaved $event)
{
$post = $event->post;
if ($post->exists || $post->user->groups()->count()) {
return;
}
$akismet = new Akismet($this->settings->get('akismet.api_key'), Core::url());
$isSpam = $akismet->isSpam(
$post->content,
$post->user->username,
$post->user->email,
null,
'comment'
);
if ($isSpam) {
$post->hide();
$this->savingPost = $post;
CommentPost::saved(function (CommentPost $post) {
if ($post !== $this->savingPost) {
return;
}
$report = new Report;
$report->post_id = $post->id;
$report->reporter = 'Akismet';
$report->reason = 'spam';
$report->time = time();
$report->save();
$this->savingPost = null;
});
}
}
}