├── demo_2_ui ├── __init__.py ├── resources │ ├── index.html │ └── module.es ├── plugin.yml └── main.py ├── demo_3_bower ├── __init__.py ├── bower.json ├── resources │ ├── js │ │ ├── module.coffee │ │ ├── routing.coffee │ │ └── controllers │ │ │ └── index.controller.coffee │ └── partial │ │ └── index.html ├── main.py └── plugin.yml ├── demo_5_widget ├── __init__.py ├── resources │ ├── js │ │ ├── module.coffee │ │ └── controllers │ │ │ └── widget.controller.coffee │ └── partial │ │ ├── widget.html │ │ └── widget.config.html ├── plugin.yml └── widget.py ├── demo_1_minimal ├── main.py ├── __init__.py └── plugin.yml ├── demo_4_http ├── __init__.py ├── resources │ ├── js │ │ ├── module.coffee │ │ ├── routing.coffee │ │ └── controllers │ │ │ └── index.controller.coffee │ └── partial │ │ └── index.html ├── plugin.yml ├── main.py └── views.py ├── .gitignore └── README.md /demo_2_ui/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import * 2 | -------------------------------------------------------------------------------- /demo_3_bower/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import * 2 | -------------------------------------------------------------------------------- /demo_5_widget/__init__.py: -------------------------------------------------------------------------------- 1 | from .widget import * 2 | -------------------------------------------------------------------------------- /demo_1_minimal/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.info('Demo plugin loaded!') -------------------------------------------------------------------------------- /demo_4_http/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import * 2 | # note the extra module 3 | from .views import * -------------------------------------------------------------------------------- /demo_4_http/resources/js/module.coffee: -------------------------------------------------------------------------------- 1 | angular.module 'ajenti.demo4', [ 2 | 'core', 3 | ] 4 | 5 | -------------------------------------------------------------------------------- /demo_1_minimal/__init__.py: -------------------------------------------------------------------------------- 1 | # This file gets imported by the core during plugin loading phase 2 | from .main import * 3 | -------------------------------------------------------------------------------- /demo_5_widget/resources/js/module.coffee: -------------------------------------------------------------------------------- 1 | angular.module 'ajenti.demo5', [ 2 | 'core', 3 | 'ajenti.dashboard', 4 | ] 5 | 6 | -------------------------------------------------------------------------------- /demo_3_bower/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plugin", 3 | "private": true, 4 | "dependencies": { 5 | "angular-chart.js": "~0.5.3" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /demo_2_ui/resources/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | Click ({{counter}}) 6 | 7 |
-------------------------------------------------------------------------------- /demo_3_bower/resources/js/module.coffee: -------------------------------------------------------------------------------- 1 | angular.module 'ajenti.demo3', [ 2 | # Note that we reference our Bower module 3 | 'core', 4 | 'chart.js', 5 | ] 6 | 7 | -------------------------------------------------------------------------------- /demo_3_bower/resources/partial/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 6 | 7 |
-------------------------------------------------------------------------------- /demo_5_widget/resources/partial/widget.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Random 4 |
5 |
6 | {{value || 'Unknown'}} 7 |
8 |
9 | -------------------------------------------------------------------------------- /demo_3_bower/resources/js/routing.coffee: -------------------------------------------------------------------------------- 1 | angular.module('ajenti.demo3').config ($routeProvider) -> 2 | $routeProvider.when '/view/demo3', 3 | templateUrl: '/demo_3_bower:resources/partial/index.html' 4 | controller: 'Demo3IndexController' 5 | -------------------------------------------------------------------------------- /demo_4_http/resources/js/routing.coffee: -------------------------------------------------------------------------------- 1 | angular.module('ajenti.demo4').config ($routeProvider) -> 2 | $routeProvider.when '/view/demo4', 3 | templateUrl: '/demo_4_http:resources/partial/index.html' 4 | controller: 'Demo4IndexController' 5 | -------------------------------------------------------------------------------- /demo_5_widget/resources/partial/widget.config.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |
-------------------------------------------------------------------------------- /demo_1_minimal/plugin.yml: -------------------------------------------------------------------------------- 1 | # This file describes the plugin 2 | name: demo_1_minimal 3 | author: Ajenti project 4 | email: e@ajenti.org 5 | url: http://ajenti.org 6 | version: '0.1' 7 | title: 'Demo 1 (minimal)' 8 | icon: question 9 | # add a dependency on the 'core' plugin (not really required in the minimal example) 10 | dependencies: 11 | - !PluginDependency { 12 | plugin_name: core 13 | } 14 | resources: [] 15 | -------------------------------------------------------------------------------- /demo_4_http/plugin.yml: -------------------------------------------------------------------------------- 1 | name: demo_4_http 2 | author: Ajenti project 3 | email: e@ajenti.org 4 | url: http://ajenti.org 5 | version: '0.1' 6 | title: 'Demo 4 (HTTP)' 7 | icon: question 8 | dependencies: 9 | - !PluginDependency { 10 | plugin_name: core 11 | } 12 | resources: 13 | - 'resources/js/module.coffee' 14 | - 'resources/js/routing.coffee' 15 | - 'resources/js/controllers/index.controller.coffee' 16 | - 'resources/partial/index.html' 17 | - 'ng:ajenti.demo4' 18 | -------------------------------------------------------------------------------- /demo_4_http/resources/js/controllers/index.controller.coffee: -------------------------------------------------------------------------------- 1 | angular.module('ajenti.demo4').controller 'Demo4IndexController', ($scope, $http, notify, pageTitle) -> 2 | pageTitle.set('Demo: HTTP') 3 | 4 | $scope.a = 8 5 | $scope.b = 3 6 | 7 | $scope.run = (method) -> 8 | $http.get("/api/demo4/calculate/#{method}/#{$scope.a}/#{$scope.b}").success (data) -> 9 | $scope.result = data 10 | .error (err) -> 11 | notify.error 'Calculation failed', err.message 12 | $scope.result = null 13 | -------------------------------------------------------------------------------- /demo_2_ui/plugin.yml: -------------------------------------------------------------------------------- 1 | # This file describes the plugin 2 | name: demo_2_ui 3 | author: Ajenti project 4 | email: e@ajenti.org 5 | url: http://ajenti.org 6 | version: '0.1' 7 | title: 'Demo 2 (UI)' 8 | icon: question 9 | # dependency on Core is required for UI plugins 10 | dependencies: 11 | - !PluginDependency { 12 | plugin_name: core 13 | } 14 | resources: 15 | - 'resources/module.es' # angular module 16 | - 'resources/index.html' # view template 17 | - 'ng:ajenti.demo2' # instructs ajenti to load Angular module 'ajenti.demo2' 18 | -------------------------------------------------------------------------------- /demo_5_widget/resources/js/controllers/widget.controller.coffee: -------------------------------------------------------------------------------- 1 | angular.module('ajenti.demo5').controller 'Demo5WidgetController', ($scope) -> 2 | # $scope.widget is our widget descriptor here 3 | $scope.$on 'widget-update', ($event, id, data) -> 4 | if id != $scope.widget.id 5 | return 6 | $scope.value = data 7 | 8 | 9 | angular.module('ajenti.demo5').controller 'Demo5WidgetConfigController', ($scope) -> 10 | # $scope.configuredWidget is our widget descriptor here 11 | $scope.configuredWidget.config.bytes ?= 4 12 | -------------------------------------------------------------------------------- /demo_5_widget/plugin.yml: -------------------------------------------------------------------------------- 1 | name: demo_5_widget 2 | author: Ajenti project 3 | email: e@ajenti.org 4 | url: http://ajenti.org 5 | version: '0.1' 6 | title: 'Demo 5 (Widget)' 7 | icon: question 8 | dependencies: 9 | - !PluginDependency { 10 | plugin_name: core 11 | } 12 | - !PluginDependency { 13 | plugin_name: dashboard 14 | } 15 | resources: 16 | - 'resources/js/module.coffee' 17 | - 'resources/js/controllers/widget.controller.coffee' 18 | - 'resources/partial/widget.html' 19 | - 'resources/partial/widget.config.html' 20 | - 'ng:ajenti.demo5' 21 | -------------------------------------------------------------------------------- /demo_4_http/main.py: -------------------------------------------------------------------------------- 1 | from jadi import component 2 | 3 | from aj.plugins.core.api.sidebar import SidebarItemProvider 4 | 5 | 6 | @component(SidebarItemProvider) 7 | class ItemProvider(SidebarItemProvider): 8 | def __init__(self, context): 9 | pass 10 | 11 | def provide(self): 12 | return [ 13 | { 14 | # attach the item to the 'general' category 15 | 'attach': 'category:general', 16 | 'name': 'Demo: HTTP', 17 | 'icon': 'question', 18 | 'url': '/view/demo4', 19 | 'children': [] 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /demo_2_ui/resources/module.es: -------------------------------------------------------------------------------- 1 | // the module should depend on 'core' to use the stock Angular components 2 | angular.module('ajenti.demo2', [ 3 | 'core', 4 | ]); 5 | 6 | angular.module('ajenti.demo2').config(($routeProvider) => { 7 | $routeProvider.when('/view/demo2', { 8 | templateUrl: '/demo_2_ui:resources/index.html', 9 | controller: 'Demo2IndexController', 10 | }); 11 | }); 12 | 13 | angular.module('ajenti.demo2').controller('Demo2IndexController', ($scope, notify, pageTitle) => { 14 | pageTitle.set('Demo: UI'); 15 | 16 | $scope.counter = 0; 17 | 18 | $scope.click = () => { 19 | $scope.counter += 1; 20 | notify.info('+1'); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /demo_2_ui/main.py: -------------------------------------------------------------------------------- 1 | from jadi import component 2 | 3 | from aj.plugins.core.api.sidebar import SidebarItemProvider 4 | 5 | 6 | @component(SidebarItemProvider) 7 | class ItemProvider(SidebarItemProvider): 8 | ''' 9 | To add a sidebar item, we create a component implementing SidebarItemProvider 10 | ''' 11 | def __init__(self, context): 12 | pass 13 | 14 | def provide(self): 15 | return [ 16 | { 17 | # attach the item to the 'general' category 18 | 'attach': 'category:general', 19 | 'name': 'Demo: UI', 20 | 'icon': 'question', 21 | 'url': '/view/demo2', 22 | 'children': [] 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /demo_3_bower/main.py: -------------------------------------------------------------------------------- 1 | from jadi import component 2 | 3 | from aj.plugins.core.api.sidebar import SidebarItemProvider 4 | 5 | 6 | @component(SidebarItemProvider) 7 | class ItemProvider(SidebarItemProvider): 8 | ''' 9 | To add a sidebar item, we create a component implementing SidebarItemProvider 10 | ''' 11 | def __init__(self, context): 12 | pass 13 | 14 | def provide(self): 15 | return [ 16 | { 17 | # attach the item to the 'general' category 18 | 'attach': 'category:general', 19 | 'name': 'Demo: Bower', 20 | 'icon': 'question', 21 | 'url': '/view/demo3', 22 | 'children': [] 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /demo_3_bower/plugin.yml: -------------------------------------------------------------------------------- 1 | name: demo_3_bower 2 | author: Ajenti project 3 | email: e@ajenti.org 4 | url: http://ajenti.org 5 | version: '0.1' 6 | title: 'Demo 3 (Bower)' 7 | icon: question 8 | dependencies: 9 | - !PluginDependency { 10 | plugin_name: core 11 | } 12 | resources: 13 | # it's important to include the required Bower resources here 14 | - 'resources/vendor/Chart.js/Chart.min.js' 15 | - 'resources/vendor/angular-chart.js/dist/angular-chart.min.js' 16 | - 'resources/vendor/angular-chart.js/dist/angular-chart.css' 17 | - 'resources/js/module.coffee' 18 | - 'resources/js/routing.coffee' 19 | - 'resources/js/controllers/index.controller.coffee' 20 | - 'resources/partial/index.html' 21 | - 'ng:ajenti.demo3' 22 | -------------------------------------------------------------------------------- /demo_5_widget/widget.py: -------------------------------------------------------------------------------- 1 | import os 2 | from jadi import component 3 | 4 | from aj.plugins.dashboard.api import Widget 5 | 6 | 7 | @component(Widget) 8 | class RandomWidget(Widget): 9 | id = 'random' 10 | 11 | # display name 12 | name = 'Random' 13 | 14 | # template of the widget 15 | template = '/demo_5_widget:resources/partial/widget.html' 16 | 17 | # template of the configuration dialog 18 | config_template = '/demo_5_widget:resources/partial/widget.config.html' 19 | 20 | def __init__(self, context): 21 | Widget.__init__(self, context) 22 | 23 | def get_value(self, config): 24 | if 'bytes' not in config: 25 | return 'Not configured' 26 | return os.urandom(int(config['bytes'])).encode('hex') 27 | -------------------------------------------------------------------------------- /demo_3_bower/resources/js/controllers/index.controller.coffee: -------------------------------------------------------------------------------- 1 | angular.module('ajenti.demo3').controller 'Demo3IndexController', ($scope, $interval, pageTitle) -> 2 | pageTitle.set('Demo: Bower') 3 | 4 | $scope.labels = ["January", "February", "March", "April", "May", "June", "July"] 5 | $scope.series = ['Series A', 'Series B'] 6 | $scope.dataTemplate = [ 7 | [65, 59, 80, 81, 56, 55, 40], 8 | [28, 48, 40, 19, 86, 27, 90] 9 | ] 10 | 11 | interval = $interval () -> 12 | $scope.data = [[], []] 13 | for i in [0...2] 14 | for j in [0...7] 15 | $scope.data[i].push $scope.dataTemplate[i][j] + Math.random() * 5 16 | , 250 17 | 18 | $scope.$on '$destroy', () -> 19 | $interval.cancel(interval) -------------------------------------------------------------------------------- /demo_4_http/resources/partial/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 |
12 | Add 13 | Divide 14 |
15 |
16 |
17 | 18 |
19 | 20 |
21 |

22 | = {{result.value}} 23 |

24 |
25 | Calculated in {{result.time|time:10}} 26 |
27 |
-------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vendor 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /demo_4_http/views.py: -------------------------------------------------------------------------------- 1 | import time 2 | from jadi import component 3 | 4 | from aj.api.http import url, HttpPlugin 5 | 6 | from aj.api.endpoint import endpoint, EndpointError, EndpointReturn 7 | 8 | 9 | @component(HttpPlugin) 10 | class Handler(HttpPlugin): 11 | def __init__(self, context): 12 | self.context = context 13 | 14 | @url(r'/api/demo4/calculate/(?P\w+)/(?P\d+)/(?P\d+)') 15 | @endpoint(api=True) 16 | def handle_api_calculate(self, http_context, operation=None, a=None, b=None): 17 | start_time = time.time() 18 | 19 | try: 20 | if operation == 'add': 21 | result = int(a) + int(b) 22 | elif operation == 'divide': 23 | result = int(a) / int(b) 24 | else: 25 | raise EndpointReturn(404) 26 | except ZeroDivisionError: 27 | raise EndpointError('Division by zero') 28 | 29 | return { 30 | 'value': result, 31 | 'time': time.time() - start_time 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ajenti demo plugins 2 | =================== 3 | 4 | This repository contains simple commented examples of Ajenti plugins. 5 | 6 | Quickstart: 7 | 8 | ``` 9 | git clone https://github.com/ajenti/demo-plugins.git 10 | cd demo-plugins 11 | 12 | sudo npm install -g babel babel-preset-es2015 less bower 13 | sudo pip install ajenti-panel ajenti-dev-multitool ajenti.plugin.core 14 | ajenti-dev-multitool --bower install 15 | ajenti-dev-multitool --build 16 | 17 | sudo ajenti-panel -v --autologin --stock-plugins --plugins . 18 | ``` 19 | 20 | Demo 1: minimal 21 | --------------- 22 | 23 | A super-minimal example of a plugin. Prints an info message to log. 24 | 25 | - [__init__.py](https://github.com/ajenti/demo-plugins/blob/master/demo_1_minimal/__init__.py#L2) 26 | - [plugin.yml](https://github.com/ajenti/demo-plugins/blob/master/demo_1_minimal/plugin.yml) 27 | 28 | Demo 2: UI 29 | ---------- 30 | 31 | A minimal UI example showing a page and some Angular.JS interaction 32 | 33 | - [dependencies & resources](https://github.com/ajenti/demo-plugins/blob/master/demo_2_ui/plugin.yml#L10) 34 | - [sidebar item](https://github.com/ajenti/demo-plugins/blob/master/demo_2_ui/main.py#L6) 35 | - [JS code](https://github.com/ajenti/demo-plugins/blob/master/demo_2_ui/resources/module.es) 36 | - [view template](https://github.com/ajenti/demo-plugins/blob/master/demo_2_ui/resources/index.html) 37 | 38 | Demo 3: Bower 39 | ------------- 40 | 41 | Example of a plugin that includes a Bower component (Chart.JS). 42 | 43 | Before running, download the component (not included): ``ajenti-dev-multitool --bower install`` 44 | 45 | - [bower.json](https://github.com/ajenti/demo-plugins/blob/master/demo_3_bower/bower.json) 46 | - [including Bower files](https://github.com/ajenti/demo-plugins/blob/master/demo_3_bower/plugin.yml#L13) 47 | - [angular.js module dependency](https://github.com/ajenti/demo-plugins/blob/master/demo_3_bower/resources/js/module.coffee#L4) 48 | 49 | Demo 4: HTTP 50 | ------------ 51 | 52 | Example of how one can add custom backend API methods 53 | 54 | - [API views](https://github.com/ajenti/demo-plugins/blob/master/demo_4_http/views.py) 55 | - [client code](https://github.com/ajenti/demo-plugins/blob/master/demo_4_http/resources/js/controllers/index.controller.coffee#L8) 56 | 57 | Demo 5: Dashboard Widget 58 | ------------------------ 59 | 60 | - [widget backend code](https://github.com/ajenti/demo-plugins/blob/master/demo_5_widget/widget.py) 61 | - [widget template](https://github.com/ajenti/demo-plugins/blob/master/demo_5_widget/resources/partial/widget.html) 62 | - [widget conf dialog template](https://github.com/ajenti/demo-plugins/blob/master/demo_5_widget/resources/partial/widget.config.html) 63 | - [controllers](https://github.com/ajenti/demo-plugins/blob/master/demo_5_widget/resources/js/controllers/widget.controller.coffee) 64 | --------------------------------------------------------------------------------