austintoddj / canvas

Publishing on your own terms
http://trycanvas.app
MIT License
3.25k stars 519 forks source link

Plugin Strategy #177

Closed tomschlick closed 5 years ago

tomschlick commented 8 years ago

Opening this ticket to discuss possible strategies in developing a plugin system.

Would help solve things like #173

jlaswell commented 7 years ago

This turned out being more content than I previously expected, so apologies there.

This is a really rough set of ideas of course, but I'm really liking the way it feels from the developer side as I keep tinkering with it. I'm trying to approach this as either a user looking to enable a plugin or as a developer wanting to create a new plugin.

# new folder structures and possible classes
app
├── Plugins
│   ├── Local
│   └── ServiceProvider.php
├── Providers
│   ├── PluginServiceProvider.php
└── Services
    ├── PluginManager.php
config
├── plugins.php

The App\Plugins namespace contains all of the interfaces and classes that a developer can expect to implement or extend when building a new plugin.

For example, App\Plugins\ServiceProvider is an abstract class that developers extend so that Canvas can register the plugin. It simply extends Laravel's native ServiceProvider to keep a common feel between Canvas and Laravel, but includes abstract methods that are required for Canvas to enable and interact with the plugin itself.

I didn't want to over-conceptualize what all could be in here, but you can imagine a common class that exposes a render() method to allow https://github.com/austintoddj/canvas/issues/173 to display a notification.


There is also an App\Plugins\Local namespace to make it easy to build and test plugins locally.

Here's an example of what the structure of a plugin could look like; you can swap Local for a Vendor name and the same applies.

Local # or Vendor
├── HelloWorld
│   ├── resources
│       ├── assets
│       ├── views
│           └── welcome.blade.php
│   ├── WelcomeMessage.php
│   └── HelloWorldServiceProvider.php

A simple config file registers each enabled plugin.

This could also be done via the admin interface, which is probably the better way to handle this process from a user's perspective.

# config/plugins.php

return [
    'providers' => [
        App\Plugins\Local\HelloWorld\HelloWorldServiceProvider::class,
        // Canvas\SurveyPlugin\SurveyServiceProvider::class,
    ]
];

The App\Providers\PluginServiceProvider is responsible for registering the App\Services\PluginManager with Canvas. This allows for the PluginManager to be accessed via app('plugins') throughout the application. The PluginManager is responsible for registering all of the plugins with Canvas, as well as providing access to each plugin when needed.

Since each plugin extends a common ServiceProvider we can start to do some fun things like automatic registration within the admin nav.

@foreach (app('plugins')->plugins() as $plugin)
    <li><a href="{{ $plugin->index() }}"><i class="zmdi zmdi-input-power"></i> {{ $plugin->name() }}</a></li>
@endforeach

example-auto-registration

While this is a trivial example, I can start to see how plugin functionality could very easily be automated, embedded within, and accessed throughout the application.

tomschlick commented 7 years ago

Seems like a good basis! One thing I would suggest is that we should come up with a list of "hooks" that a plugin can insert itself into in the interface.

Wordpress for instance lets you say "Insert this new button on the top toolbar" or "Insert this html snipped at the end of a post", etc. This list would need to account for both admin pages as well as the frontend display logic.

jlaswell commented 7 years ago

Thanks! Much agreed on the concept of hooks. I'm not familiar with plugins in Wordpress, but I like the idea that certain areas are available. We may find use for taking that in a similar direction as well by exposing certain events for plugins to listen for or emit, if someone sees value there. I'll update with a basic list later.

tomschlick commented 7 years ago

Yeah we would need both lists (hooks and events), so thats a good starting point. From there we can start creating tasks to keep track of it all.

Jeroen-G commented 7 years ago

What problem do you want to fix that cannot be solved with events or packages?

jlaswell commented 7 years ago

@Jeroen-G That's actually what I see being used to add new plugins. I think you're exactly right that we should be using those to as the mechanism here. I think more of the issue is how to make it easiest for developers to build a package that a user can click and install from within Canvas.

Jeroen-G commented 7 years ago

If that is the case, the problem is making installation of packages easy. Which is not impossible. With one of my packages, Laravel Packager, I can install automatically create/install packages in Laravel. I think you should focus on a 'policy' regarding events and an easy way to install packages dedicated to adding functionality to Canvas instead of figuring out a way to code a sort of duplication of the event system.

tomschlick commented 7 years ago

@Jeroen-G yeah the idea is to come up with a list of events we would fire from the system, and a list of hooks for rendering UI elements. Both could probably use the base laravel events system but we still need to define what those integration points are.

foxted commented 7 years ago

Just throwing an idea out there, but Canvas seems to be developer oriented. As developers, we are pretty familiar with installing a package through composer and adding the Service Provider in config/app.php.

The question is: is the plan for Canvas to be a regular CMS (like WordPress), targeted to any user (including non-developers) or a developer-specific CMS?

My opinion would be to keep the developer focus, as there are already way to many CMS out there for non-developers and their implementation of "plugins" is what makes these platforms suck in the long run (looking at you WordPress!).

What do you guys think?

austintoddj commented 7 years ago

@foxted Have to say I agree with you here. I like the idea of extensibility with Canvas, as it would open it up to a greater audience, however at its core, Canvas remains a markdown supported, syntax-highlighted, Laravel application, all of which scream developer-oriented.

So, is there a happy middle-ground? I would consider applications like OctoberCMS a more developer oriented program, yet they implement plugins, but remain pretty lightning-fast, avoiding that bloat that comes with mainstream CMS's. Perhaps there is a developer-friendly way to offer the option of extending the app? Or is that an oxymoron?

Thoughts?

victorpierredev commented 7 years ago

Why not integrate something like Caffeinated/modules to handle the plugin management. I've been using it on client's project to make the application more modular and reusable and I'm really enjoying (but I'm in early stages of getting comfortable with it).

I've integrated it with the backend of the-control-group/voyager and I don't see why the same could not be applied to Canvas.

Caffeinated/modules have the following structure:

laravel-project/
    app/
    |-- Modules/
        |-- Blog/
            |-- Console/
            |-- Database/
                |-- Migrations/
                |-- Seeds/
            |-- Http/
                |-- Controllers/
                |-- Middleware/
                |-- Requests/
                |-- routes.php
            |-- Providers/
                |-- BlogServiceProvider.php
                |-- RouteServiceProvider.php
            |-- Resources/
                |-- Lang/
                |-- Views/
            |-- module.json
austintoddj commented 7 years ago

@vpakg Thanks for the suggestion! The reason it hasn't been done yet is really because I haven't had the time or thought out the process. Since you have such a good start to go on, would you want to try to send a PR with a feature like this? I'd be more than happy to integrate such a thing into the project.

austintoddj commented 5 years ago

First off, thanks for taking the time to create the issue. Closing because everything v3.x related will remain as-is and won’t receive anymore updates. The next release is slated for this week, so stay tuned!