GithubHelp home page GithubHelp logo

hootlex / laravel-moderation Goto Github PK

View Code? Open in Web Editor NEW
520.0 15.0 68.0 55 KB

A simple Content Moderation System for Laravel 5.* that allows you to Approve or Reject resources like posts, comments, users, etc.

License: MIT License

PHP 100.00%
laravel moderation laravel-moderation eloquent

laravel-moderation's Introduction

Laravel Moderation Build Status Version Total Downloads Software License

A simple Moderation System for Laravel 5.* that allows you to Approve or Reject resources like posts, comments, users, etc.

Keep your application pure by preventing offensive, irrelevant, or insulting content.

Possible Use Case

  1. User creates a resource (a post, a comment or any Eloquent Model).

  2. The resource is pending and invisible in website (ex. Post::all() returns only approved posts).

  3. Moderator decides if the resource will be approved, rejected or postponed.

  4. Approved: Resource is now public and queryable.

  5. Rejected: Resource will be excluded from all queries. Rejected resources will be returned only if you scope a query to include them. (scope: withRejected)

  6. Postponed: Resource will be excluded from all queries until Moderator decides to approve it.

  7. You application is clean.

Installation

First, install the package through Composer.

composer require hootlex/laravel-moderation

If you are using Laravel < 5.5, you need to add Hootlex\Moderation\ModerationServiceProvider to your config/app.php providers array:

'providers' => [
    ...
    Hootlex\Moderation\ModerationServiceProvider::class,
    ...
];

Lastly you publish the config file.

php artisan vendor:publish --provider="Hootlex\Moderation\ModerationServiceProvider" --tag=config

Prepare Model

To enable moderation for a model, use the Hootlex\Moderation\Moderatable trait on the model and add the status, moderated_by and moderated_at columns to your model's table.

use Hootlex\Moderation\Moderatable;
class Post extends Model
{
    use Moderatable;
    ...
}

Create a migration to add the new columns. (You can use custom names for the moderation columns)

Example Migration:

class AddModerationColumnsToPostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->smallInteger('status')->default(0);
            $table->dateTime('moderated_at')->nullable();
            //To track who moderated the Model, add 'moderated_by' and set the column name in the config file.
            //$table->integer('moderated_by')->nullable()->unsigned();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('posts', function(Blueprint $table)
        {
            $table->dropColumn('status');
            $table->dropColumn('moderated_at');
            //$table->dropColumn('moderated_by');
        });
    }
}

You are ready to go!

Usage

Note: In next examples I will use Post model to demonstrate how the query builder works. You can Moderate any Eloquent Model, even User.

Moderate Models

You can moderate a model Instance:

$post->markApproved();

$post->markRejected();

$post->markPostponed();

$post->markPending();

or by referencing it's id

Post::approve($post->id);

Post::reject($post->id);

Post::postpone($post->id);

or by making a query.

Post::where('title', 'Horse')->approve();

Post::where('title', 'Horse')->reject();

Post::where('title', 'Horse')->postpone();

Query Models

By default only Approved models will be returned on queries. To change this behavior check the configuration.

To query the Approved Posts, run your queries as always.
//it will return all Approved Posts (strict mode)
Post::all();

// when not in strict mode
Post::approved()->get();

//it will return Approved Posts where title is Horse
Post::where('title', 'Horse')->get();
Query pending or rejected models.
//it will return all Pending Posts
Post::pending()->get();

//it will return all Rejected Posts
Post::rejected()->get();

//it will return all Postponed Posts
Post::postponed()->get();

//it will return Approved and Pending Posts
Post::withPending()->get();

//it will return Approved and Rejected Posts
Post::withRejected()->get();

//it will return Approved and Postponed Posts
Post::withPostponed()->get();
Query ALL models
//it will return all Posts
Post::withAnyStatus()->get();

//it will return all Posts where title is Horse
Post::withAnyStatus()->where('title', 'Horse')->get();

Model Status

To check the status of a model there are 3 helper methods which return a boolean value.

//check if a model is pending
$post->isPending();

//check if a model is approved
$post->isApproved();

//check if a model is rejected
$post->isRejected();

//check if a model is rejected
$post->isPostponed();

Strict Moderation

Strict Moderation means that only Approved resource will be queried. To query Pending resources along with Approved you have to disable Strict Moderation. See how you can do this in the configuration.

Configuration

Global Configuration

To configuration Moderation package globally you have to edit config/moderation.php. Inside moderation.php you can configure the following:

  1. status_column represents the default column 'status' in the database.
  2. moderated_at_column represents the default column 'moderated_at' in the database.
  3. moderated_by_column represents the default column 'moderated_by' in the database.
  4. strict represents Strict Moderation.

Model Configuration

Inside your Model you can define some variables to overwrite Global Settings.

To overwrite status column define:

const MODERATION_STATUS = 'moderation_status';

To overwrite moderated_at column define:

const MODERATED_AT = 'mod_at';

To overwrite moderated_by column define:

const MODERATED_BY = 'mod_by';

To enable or disable Strict Moderation:

public static $strictModeration = true;

laravel-moderation's People

Contributors

bryant1410 avatar gabrielfiel avatar gecche avatar harrygulliford avatar hootlex avatar piranhageorge avatar stephane-monnot avatar superbiche avatar zek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-moderation's Issues

Scopes don't work as expected in relations

When strict mode is off rejected items are excluded by default through a global scope on the boot method. This is to be expected, however, it doesn't appear possible to include them using the withRejected() local scope.

$user->persona->withRejected()->first(); returns null if persona is rejected as the global scope supersedes the local one; the where status != 2 clause excludes rejected records despite the local scope where status in (1,2) including them.

$post->markPending();

laravel 5.3
$post->markPending();

return error

FatalErrorException in Moderatable.php line 98:
Call to a member function attributesToArray() on integer

2 suggestions ?

Hi,

Great package, but I cannot use on my projects because you used the "status" attribute and I already use it almost everywhere with my own status.

  • So maybe it's better to rename it to moderation_status or something else?
  • And is this working with the update event? So when a post is updated, the moderator/admin have to "accept" the changes.

Requesting Help

I have a question that has nothing to do with a issue of package but instead just my noobness needing some help.

Heres my view: (displays all torrents pending)

@extends('layout.default') 

@section('title')
<title>Torrents - {{ Config::get('other.title') }}</title>
@stop 

@section('breadcrumb')
<li class="active">
  <a href="{!! route('moderation') !!}" itemprop="url" class="l-breadcrumb-item-link">
    <span itemprop="title" class="l-breadcrumb-item-link-title">Moderation</span>
  </a>
</li>
@stop 

@section('content')
<div class="container box">
  <div class="torrents col-md-12">
    <table class="table table-bordered table-hover">
      <thead>
        <tr>
          <th>Category</th>
          <th>Name</th>
          <th>Type</th>
          <th>Size</th>
          <th>Uploader</th>
          <th>Approve</th>
          <th>Reject</th>
          <th>Postpone</th>
        </tr>
      </thead>
      <tbody>
        @foreach($pending as $p)
        <tr>
        <td>{{ $p->category->name }}</td>
        <td>{{ $p->name }}</td>
        <td>{{ $p->type }}</td>
        <td>{{ $p->getSize() }}</td>
        <td>{{ $p->user->username }}</td>
        <td><button class="btn btn-labeled btn-success" type="button"><span class="btn-label"><i class="fa fa-thumbs-up"></i></span>Approve</button></td>
        <td><button class="btn btn-labeled btn-danger" type="button"><span class="btn-label"><i class="fa fa-thumbs-down"></i></span>Reject</button></td>
        <td><button class="btn btn-labeled btn-warning" type="button"><span class="btn-label"><i class="fa fa-stop"></i></span>Postpone</button></td>
        </tr>
        @endforeach
      </tbody>
    </table>
  </div>
</div>
@stop

Heres part of my controller that does this:

        /**
    * Torrent Moderation
    *
    * @access public
    * @param $slug Slug of the torrent
    * @param $id Id of the torrent
    *
    */
    public function moderation()
    {
        $approve = Torrent::where('name', '=', Request::get('name'))->approve();
        $pending = Torrent::pending()->get(); //returns all Pending Torrents
        return view('torrent.moderation', ['pending' => $pending, 'approve' => $approve]);
    }

Heres a pic of the view:
http://i.imgur.com/6B3i06u.png?1

Question Is:
How can I make it so my Approve Button when clicked approves the torrent in question. I added this to controller $approve = Torrent::where('name', '=', Request::get('name'))->approve(); but don't know how to apply it to a button.

Publish new tag version

Hi, can you publish a new tag for Laravel 5.3 users? I dont feel very confident when pulling dev-master branches in production environment. ๐Ÿ˜…

BTW: Really like this package! Thanks for that!

Trait method performDeleteOnModel collides with SoftDelete trait

Trait method performDeleteOnModel has not been applied, because there are collisions with other trait methods on User.

\Illuminate\Database\Eloquent\SoftDeletes has the same method protected function performDeleteOnModel(). Can I use these together somehow? Should I just use the method from the soft delete trait?


Small update: both traits also define the $forceDeleting property, which cannot be rewritten or aliased. Why exactly is it being use? There's no need for it?

Laravel 7

Hi,

may i know if this plugin compatible with Laravel 7?

I have this error whenever I try to get the row data.

   $post = Post::where('id', $id)->first();

Illuminate\Database\QueryException
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'posts.' in 'where clause' (SQL: select * from posts where id = 1 and posts.`` != 2 limit 1)

Adding events

Thinking about status, etc. it might be a good idea to add events for when a status changes.

Think about an author getting a notification that his article is back from a review.
Or maybe it is published now, etc.

Using the laravel event system other parts of the app can easily subscribe to the statusHasChanged event and act on it, e.g. send out a notification, log something to a database, add something to a queue, ...

Is this maintained?

If you don't have time to maintain this I do not mind maintaining this package.

Moderation Trait Issue

I have a model and I added the Hootlex\Moderation\Moderatable trait as below

use Hootlex\Moderation\Moderatable;
class Post extends Model
{
    use Moderatable;
    ...
}

But my index blade that returns a list of submitted posts does not show unless I remove use Moderatable; from my model. So instead I added the trait to my controller, but now when I try to approve the post it gives this error Call to undefined method pending() reject() or postpone() . I also tried other ways to moderate an instance of a model provided in the documentation, and I get the same error. Please assist on a solution for thus error

Unable to update rejected models

It seems if a model is rejected it can not be updated or I'm getting it wrong. Anyone with an idea how to solve this, thanks in advanced

Moderated By (Question)

How can I customize when I approve a model and manually assign the user for moderated_by like
$model->moderated_by = 'String user id' ?

Support for Laravel Nova

Is it possible to gather all entities within Laravel Nova while bypassing status moderation? I already disabled strict moderation, no luck though.

Undefined index: column

hi @hootlex
I have encountered this error.

    "message": "Undefined index: column",
    "exception": "ErrorException",
    "file": "...\\vendor\\hootlex\\laravel-moderation\\src\\ModerationScope.php",
    "line": 404,

In ModerationScope.php are you sure always has attribute in $where array

    protected function isModerationConstraint(array $where, $column)
    {
            return $where['column'] == $column;
    }

Issue with Eloquent\Scope - Laravel 5.1 compatibility issue

Hi All,

I am using Laravel 5.1 and getting the following issue with this package.

"Interface 'Illuminate\\Database\\Eloquent\\Scope' not found",

// error details
"file": "/home/vagrant/projects/laravel/MyApp/vendor/hootlex/laravel-moderation/src/ModerationScope.php",
    "class": "Symfony\\Component\\Debug\\Exception\\FatalErrorException",

In my Model I am including it as

use Hootlex\Moderation\Moderatable;

class Collection extends Model
{
    use Moderatable;
}

Please let me know whats wrong with this?

Kind regards,
Javed Gardezi

Route-Model-Binding in Strict Mode?

I get page not found errors when trying to access a model for editing that hasn't been approved yet.
I'm using restful routes and controllers, along with route-model-binding. I suspect that the query for the route-model-binding is preventing me from accessing that model, because it hasn't been approved. Is there a recommended process for this? How can I access a record for approval (via route-model-binding) if that doesn't work?

Thanks!

Sample Blades?

Do you plan on adding any sample blade views for noobs like me?

Actions on a single instance

Perhaps an idea to allow $user->approve(), $user->reject, $user->postpone(), et cetera? :)

Oh, and scopes! That way it can be combined with other query limitations like wheres and whatnot.

Difference Postponed/Pending

Hello Hotlex, thanks for this package, I'm interesting to adopt it in my project.

I have not a lot of experience in content moderation so can you give more details about difference from Postponed and Pending status? It seams a redundant definition.

Really thanks
Valerio

Usage of having multiple moderators to 1 row?

Is there a way to have more than one moderator per model row?

As far as I can tell, it's more of a boolean of approved/declined etc for a row, without the ability to set, say, 5 users who are required to approve it in a tree format so user 5 approves, then user 4 if user 5 approves, then user 3 once 4 approves, and so on.

Possibility to use custom status

Hey, very nice package. However, if this flow does not work with your process, you need to hack it.

What do you think about adding a possibility to add a custom status?

E.g.

$post->setStatus('custom');

Post::all()->withStatus('custom');

Maybe the user can set those status and decide if they should be queried in strict mode or not.

You could even use magic methods to build markCustom methods.

What do you think?

Update database only after confirmation?

I was thinking about writing a package like this one but with one additional feature : update the model table only when the changes are confirmed.

Problem

Imagine a blog where members can write articles, after the article is confirmed, it will be shown on the homepage.
But what if this member want to edit this article?
He update it and wait for the confirmation before the article will be shown on the homepage again (STRICT MODE).

Solution

In order to avoid this "delay", when the article is updated by the member, into the database it shouldn't be modified before the post is confirmed. Instead, a new table should save the updated object and wait for confirmation.

Hope you understood me.

Building a Filter Query for ModerationStatus::APPROVED

Hello I am maintaining a project that uses this library for moderation, and on some of our content we like to filter by 'Approved', 'Pending' , 'Rejected', and 'Postponed'.

We have a custom Status Filter that extends Laravel\Nova\Filters\Filter. In this class, we define an apply method as such:

    public function apply(Request $request, $query, $value)
    {
        switch($value) {
            case $this->all:
            default:
                $query = $query->withAnyStatus();
                break;
            case ModerationStatus::PENDING:
                $query = $query->pending();
                break;
            case ModerationStatus::APPROVED:
                break;
            case ModerationStatus::REJECTED:
                $query = $query->rejected();
                break;
            case ModerationStatus::POSTPONED:
                $query = $query->postponed();
                break;
        }

        return $query;
    }

Now I am sorry if this is the wrong place to be asking this, but as it pertains to Laravel and Nova, how can I add an approved() filter to the $query() that is being returned? I see this query is of type \Illuminate\Database\Eloquent\Builder and I kind of half expected to see this library extend that somewhere in the code. This however is not the case and digging through the API reference for \Illuminate\Database\Eloquent\Builder I don't see any reference to these methods being performed on the $query in the above function.

Could someone maybe point me in the right direction of where to look? Is hootlex not fully compatible with Laravel and Nova and therefore doesn't offer a way to filter by ModerationStatus::APPROVED ?

Any help would be greatly appreciated

markApproved, markRejected, ... functions incompatible with models with attributes with array casts

I have a model with an array cast on one attribute foo.

protected $casts = [
        'foo' => 'array',
];

But the SQL type is VARCHAR.
When the any of markApproved, markRejected, markPostponed, markPending is called, it causes the attributes array to be changed from

$attributes = [
    ...
    'foo' => '["mango","pineapple","pawpaw"]'
    ...
]

to

$attributes = [
    ...
    'foo' => [
        "mango", "pineapple", "pawpaw"
    ]
    ...
]

which leads to unexpected value exceptions when trying to save the model

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.