├── composer.json
├── src
├── PageController.php
└── ItemList.php
└── README.md
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "akerbel/whmcs-easy-addons",
3 | "version": "v1.2.0",
4 | "description": "Whmcs Addon Admin Page Controller",
5 | "keywords": ["whmcs"],
6 | "homepage": "https://github.com/akerbel/whmcs-easy-addons",
7 | "authors": [
8 | {
9 | "name": "Anton Kerbel",
10 | "email": "apefromfuture@gmail.com"
11 | }
12 | ],
13 | "autoload": {
14 | "psr-4": {
15 | "whmcsEasyAddons\\": "src/"
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PageController.php:
--------------------------------------------------------------------------------
1 | view = new \Smarty();
37 | $this->view->template_dir = ROOTDIR.'/modules/addons/'.$module.'/templates/';
38 | $this->view->compile_dir = $templates_compiledir;
39 |
40 | // Assing WHMCS system params
41 | $this->view->assign('_LANG', $_LANG);
42 | $this->view->assign('_CONFIG', $CONFIG);
43 | $this->view->assign('csrfToken', generate_token('plain'));
44 |
45 | // Assing our module params
46 | $this->vars = $vars;
47 | $this->view->assign('vars', $this->vars);
48 | $this->view->assign('customadminpath', $customadminpath);
49 | $this->modulelink = '/'.$customadminpath.'/addonmodules.php?module='.$module;
50 | $this->view->assign('modulelink', $this->modulelink);
51 | if (isset($_REQUEST['action'])) {
52 | $this->action = $_REQUEST['action'];
53 | }
54 | $this->view->assign('action', $this->action);
55 | }
56 |
57 | /**
58 | * Execute chosen tabs action.
59 | *
60 | * @throws \Exception If it is not possible to find a method for chosen action.
61 | */
62 | public function run()
63 | {
64 | // Show tabs
65 | $this->_displayMenu();
66 |
67 | // Try to execute action method
68 | $methodName = $this->action.'Action';
69 | try {
70 | if (method_exists($this, $methodName)) {
71 | $this->$methodName();
72 | echo $this->view->fetch($this->action.'.tpl');
73 | } else {
74 | throw new \Exception('No controller for '.$this->action);
75 | }
76 | } catch (\Exception $e) {
77 | echo $e->getMessage();
78 | }
79 | }
80 |
81 | /**
82 | * Generate and echo html-code for tabs.
83 | */
84 | private function _displayMenu()
85 | {
86 | if (count($this->menu)) {
87 | $menu = '
';
88 | foreach ($this->menu as $title => $element) {
89 | if ($this->action == $element) {
90 | $menu .= '- ';
91 | } else {
92 | $menu .= '
- ';
93 | }
94 | $menu .= ''.$title.'';
95 | $menu .= '
';
96 | }
97 | $menu .= '
';
98 | echo $menu;
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | WHMCS Easy Addons
2 | =================
3 |
4 | WHMCS Easy Addons is a package for fast creating of addon admin pages.
5 |
6 | Homepage: https://github.com/akerbel/whmcs-easy-addons
7 |
8 | How to install:
9 | ===============
10 | The recommended way to install WHMCS Easy Addons is through Composer.
11 |
12 | ```bash
13 | # Install Composer
14 | curl -sS https://getcomposer.org/installer | php
15 | ```
16 |
17 | Next, run the Composer command to install the latest stable version of WHMCS Easy Addons:
18 |
19 | ```bash
20 | php composer.phar require akerbel/whmcs-easy-addons
21 | ```
22 |
23 | How to use:
24 | ==============
25 | I. Admin tabs.
26 | - Install Composer
27 | ```bash
28 | curl -sS https://getcomposer.org/installer | php
29 | ```
30 | - Create composer.json file inside your addon:
31 | ```json
32 | {
33 | "name": "EasyAddonsSkeleton",
34 | "version": "1.0.0",
35 | "description": "An example addon for WHMCS Easy Addons",
36 | "autoload": {
37 | "psr-4": {
38 | "EasyAddonsSkeleton\\": "src/"
39 | }
40 | },
41 | "require": {
42 | "akerbel/whmcs-easy-addons": "1.*"
43 | }
44 | }
45 | ```
46 | - Your "_output" function should look like this:
47 | ```php
48 | function EasyAddonsSkeleton_output($vars) {
49 | global $module;
50 | include_once(ROOTDIR."/modules/addons/".$module.'/src/output.php');
51 | }
52 | ```
53 | - Create 'src' folder.
54 | - Create output.php file in 'src' folder.
55 |
56 | output.php:
57 | ```php
58 | namespace EasyAddonsSkeleton;
59 | global $module;
60 | include_once(ROOTDIR."/modules/addons/".$module.'/vendor/autoload.php');
61 |
62 | $EasyAddonsSkeleton = new EasyAddonsSkeletonController($vars);
63 | $EasyAddonsSkeleton->run();
64 | ```
65 | - Create EasyAddonsSkeletonController.php in 'src' folder.
66 |
67 | EasyAddonsSkeletonController.php:
68 | ```php
69 | namespace EasyAddonsSkeleton;
70 |
71 | use whmcsEasyAddons;
72 |
73 | class EasyAddonsSkeletonController extends whmcsEasyAddons\PageController {
74 | // This tab will be chosen by dafault
75 | public $action = 'firsttab';
76 |
77 | // This is array of your tabs and their names.
78 | public $menu = array(
79 | 'First Tab' => 'firsttab',
80 | 'Second Tab' => 'secondtab',
81 | );
82 |
83 | public function firsttabAction(){
84 |
85 | // Your code for "First tab"
86 |
87 | }
88 |
89 | public function secondtabAction(){
90 |
91 | // Your code for "Second tab"
92 |
93 | }
94 | }
95 | ```
96 | - Create folder 'templates'.
97 | - Create .tpl file for each your tab in 'templates' folder with names like 'firsttab.tpl' and 'secondtab.tpl'.
98 | - You can assing your php variables in each 'Action' method:
99 | ```php
100 | $this->view->assign('smarty_variable', $your_php_variable);
101 | ```
102 |
103 | ====================
104 | II. Fast item lists.
105 | - Example code:
106 | ```php
107 | $list = new whmcsEasyAddons\ItemList(
108 |
109 | // SQL params array. Read more in ItemList.php
110 | array(
111 | 'SELECT' => '*',
112 | 'FROM' => 'tblmodulelog',
113 | ),
114 |
115 | // Filters array. Read more in ItemList.php . Optional.
116 | array(
117 | array(
118 | 'name' => 'module', 'value' => $_GET['filter']['module'], 'description' => 'module'
119 | ),
120 | array(
121 | 'name' => 'action', 'value' => $_GET['filter']['action'], 'description' => 'action'
122 | ),
123 | array(
124 | 'name' => 'request', 'value' => $_GET['filter']['request'], 'description' => 'request', 'type' => 'LIKE'
125 | ),
126 | array(
127 | 'name' => 'response', 'value' => $_GET['filter']['response'], 'description' => 'response', 'type' => 'LIKE'
128 | ),
129 | ),
130 |
131 | // Paginators type. Read more in a changelog for v1.2 . Optional.
132 | 'dafault'
133 | );
134 | $this->view->assign('result', $list->result);
135 | $this->view->assign('paginator', $list->paginator);
136 | $this->view->assign('tablehead', $list->tablehead);
137 | $this->view->assign('filter', $list->filter);
138 | ```
139 | - Now you can use this variables in your .tpl file:
140 |
141 | 1. $filter - show a filter form.
142 | 2. $paginator - show a page navigation.
143 | 3. $tablehead - show a tablehead with sort buttons.
144 | 4. $result - show an array of items.
145 |
146 | New in v1.1:
147 | ==============
148 | 1. Now JOIN parameters can be arrays. It means, now you can use few similar JOINs in your query.
149 | 2. ORDER and SORT parameters was added . If you use JOINs in your query, you must define you ORDER parameter, because 'ORDER BY' statement will use default 'id' and it will not work with JOINs.
150 |
151 | Fixes in v1.1:
152 | ==============
153 | 1. The bug with namespaces of Exception class was fixed.
154 | 2. Spacebars was added between pages in a paginator.
155 |
156 | New in v1.2:
157 | ==============
158 | 1. Types of the paginator added. Enabled 'default' and 'short'.
159 | 'default' type is an old paginator with numbers.
160 | 'short' type is a new fast paginator. Use it if you work with a very big table. It includes only two buttons: previous page and next page. Stylized as a default WHMCS module logs` paginator.
--------------------------------------------------------------------------------
/src/ItemList.php:
--------------------------------------------------------------------------------
1 | sql_array = $sql;
84 | $this->filter_params = $filter_params;
85 | $this->paginator_type = $paginator_type;
86 | $this->createList();
87 | }
88 |
89 | /**
90 | * Get items from a database and create a list.
91 | */
92 | public function createList()
93 | {
94 | global $whmcsmysql;
95 |
96 | // Get an information about sorting and chosen page.
97 | if ($_GET['page'] != null) {
98 | $this->page = $_GET['page'];
99 | }
100 | if ($_GET['order'] != null) {
101 | $this->order = $_GET['order'];
102 | } elseif ($this->sql_array['ORDER']) {
103 | $this->order = $this->sql_array['ORDER'];
104 | }
105 | if ($_GET['sort'] != null) {
106 | $this->sort = $_GET['sort'];
107 | } elseif ($this->sql_array['SORT']) {
108 | $this->sort = $this->sql_array['SORT'];
109 | }
110 |
111 | // Include filters to request.
112 | if (count($this->filter_params) and is_array($this->filter_params)) {
113 | foreach ($this->filter_params as $param) {
114 | if ($param['value']) {
115 | if ($param['type'] == 'LIKE') {
116 | $this->sql_array['WHERE'] .= ' '.$param['name'].' LIKE "%'.$param['value'].'%" ';
117 | } elseif ($param['type'] == 'IN') {
118 | $this->sql_array['WHERE'] .= ' '.$param['name'].' IN ('.$param['value'].') ';
119 | } else {
120 | $this->sql_array['WHERE'] .= ' '.$param['name'].' = "'.$param['value'].'" ';
121 | }
122 | }
123 | }
124 | }
125 |
126 | // Create SQL.
127 | $this->raw_sql =
128 | ' SELECT '.$this->sql_array['SELECT'].
129 | ' FROM '.$this->sql_array['FROM'].
130 | (isset($this->sql_array['INNER JOIN']) ?
131 | (is_array($this->sql_array['INNER JOIN'])) ?
132 | ' INNER JOIN '.implode(' INNER JOIN ', $this->sql_array['INNER JOIN'])
133 | : ' INNER JOIN '.$this->sql_array['INNER JOIN']
134 | : ''
135 | ).
136 | (isset($this->sql_array['LEFT JOIN']) ?
137 | (is_array($this->sql_array['LEFT JOIN'])) ?
138 | ' LEFT JOIN '.implode(' LEFT JOIN ', $this->sql_array['LEFT JOIN'])
139 | : ' LEFT JOIN '.$this->sql_array['LEFT JOIN']
140 | : ''
141 | ).
142 | (isset($this->sql_array['RIGHT JOIN']) ?
143 | (is_array($this->sql_array['RIGHT JOIN'])) ?
144 | ' RIGHT JOIN '.implode(' RIGHT JOIN ', $this->sql_array['RIGHT JOIN'])
145 | : ' RIGHT JOIN '.$this->sql_array['RIGHT JOIN']
146 | : ''
147 | ).
148 | (isset($this->sql_array['WHERE']) ? ' WHERE '.$this->sql_array['WHERE'] : '').
149 | ' ORDER BY '.$this->order.' '.$this->sort.
150 | ' LIMIT '.(($this->page - 1) * $this->perpage).', '.$this->perpage.'
151 | ';
152 |
153 | // Do SQL request.
154 | $result_data = full_query($this->raw_sql);
155 | $result = array();
156 |
157 | // Fetch answer.
158 | while ($line = mysql_fetch_assoc($result_data)) {
159 | $result[] = $line;
160 | }
161 | $this->result = $result;
162 |
163 | // Create concomitants.
164 | $this->createTablehead();
165 | $this->createPaginator();
166 | $this->createFilter();
167 | }
168 |
169 | /**
170 | * Create a tablehead with sort buttons.
171 | */
172 | private function createTablehead()
173 | {
174 | $th = '';
175 | if (isset($this->result[0]) and is_array($this->result[0])) {
176 | $th .= '';
177 | foreach (array_keys($this->result[0]) as $head) {
178 | $th .= '';
179 | if ($this->order != $head) {
180 | $th .= ''.$head.'';
181 | } else {
182 | $th .= '';
183 | $th .= $head;
184 | $th .= ' ';
185 | $th .= '';
186 | }
187 | $th .= ' | ';
188 | }
189 | $th .= '
';
190 | }
191 | $this->tablehead = $th;
192 | }
193 |
194 | /**
195 | * Create the paginator.
196 | */
197 | private function createPaginator()
198 | {
199 |
200 | // If sql_array doesn`t contain 'FROM' parameter, we can`t create the paginator.
201 | if ($this->sql_array['FROM']) {
202 |
203 | $res = '';
204 |
205 | // Make a short form of the paginator
206 | if ($this->paginator_type == 'short') {
207 |
208 | $res .= '';
221 |
222 | // Make a default form of the paginator
223 | } else {
224 |
225 | // Do request about a count of items in table
226 | $count = mysql_fetch_assoc(full_query('
227 | SELECT count(*) AS count
228 | FROM '.$this->sql_array['FROM'].
229 | (isset($this->sql_array['INNER JOIN']) ?
230 | (is_array($this->sql_array['INNER JOIN'])) ?
231 | ' INNER JOIN '.implode(' INNER JOIN ', $this->sql_array['INNER JOIN'])
232 | : ' INNER JOIN '.$this->sql_array['INNER JOIN']
233 | : ''
234 | ).
235 | (isset($this->sql_array['LEFT JOIN']) ?
236 | (is_array($this->sql_array['LEFT JOIN'])) ?
237 | ' LEFT JOIN '.implode(' LEFT JOIN ', $this->sql_array['LEFT JOIN'])
238 | : ' LEFT JOIN '.$this->sql_array['LEFT JOIN']
239 | : ''
240 | ).
241 | (isset($this->sql_array['RIGHT JOIN']) ?
242 | (is_array($this->sql_array['RIGHT JOIN'])) ?
243 | ' RIGHT JOIN '.implode(' RIGHT JOIN ', $this->sql_array['RIGHT JOIN'])
244 | : ' RIGHT JOIN '.$this->sql_array['RIGHT JOIN']
245 | : ''
246 | ).
247 | (isset($this->sql_array['WHERE']) ? ' WHERE '.$this->sql_array['WHERE'] : '')
248 | ));
249 | $this->count = $count['count'];
250 |
251 | // Count of pages
252 | $this->maxpage = floor($count['count'] / $this->perpage);
253 |
254 | // If we have an incomplete page in the end, when do maxpage+1
255 | if (ceil($count['count'] / $this->perpage) != $this->maxpage) {
256 | $this->maxpage += 1;
257 | }
258 |
259 | // Create html-code of the paginator.
260 |
261 | // Previous page
262 | if ($this->page > 1) {
263 | $res .= '<< ';
264 | }
265 | // First page
266 | if ($this->page - $this->pages_in_paginator > 1) {
267 | $res .= '1 .. ';
268 | }
269 | // Simple pages
270 | for ($i = $this->page - $this->pages_in_paginator; $i <= $this->page + $this->pages_in_paginator; ++$i) {
271 | if (($i > 0) and ($i <= $this->maxpage)) {
272 | // Simple page
273 | if ($i != $this->page) {
274 | $res .= ''.$i.' ';
275 | // Chosen page
276 | } else {
277 | $res .= ''.$i.' ';
278 | }
279 | }
280 | }
281 | // Last page
282 | if ($this->page + $this->pages_in_paginator < $this->maxpage) {
283 | $res .= '.. '.$this->maxpage.' ';
284 | }
285 | // Next page
286 | if ($this->page < $this->maxpage) {
287 | $res .= '>> ';
288 | }
289 | }
290 |
291 | $this->paginator = $res;
292 | } else {
293 | $this->paginator = 'You should pass the value "FROM"';
294 | }
295 | }
296 |
297 | /**
298 | * Create a filter-code.
299 | */
300 | private function createFilter()
301 | {
302 | $filter = '';
323 |
324 | $filter .= '
';
325 |
326 | $filter .= '';
332 |
333 | $this->filter = $filter;
334 | }
335 |
336 | /**
337 | * Create URL for a page with all sorting and filter parameters.
338 | *
339 | * @param array $newdata The array of parameters, which values is different from default values.
340 | */
341 | public function getUrl($newdata = array())
342 | {
343 | global $customadminpath, $module;
344 | $data = array(
345 | 'page' => $this->page,
346 | 'order' => $this->order,
347 | 'sort' => $this->sort,
348 | );
349 | foreach ($_GET as $k => $v) {
350 | $data[$k] = $v;
351 | }
352 | foreach ($newdata as $k => $v) {
353 | $data[$k] = $v;
354 | }
355 | $url = '/'.$customadminpath.'/addonmodules.php?';
356 | foreach ($data as $key => $value) {
357 | if (is_array($value)) {
358 | foreach ($value as $k => $v) {
359 | $url .= '&'.$key.'['.$k.']='.$v;
360 | }
361 | } else {
362 | $url .= '&'.$key.'='.$value;
363 | }
364 | }
365 |
366 | return $url;
367 | }
368 |
369 | /**
370 | * Toggle sort direction.
371 | *
372 | * @param string $sort The sort direction (DESC|ASC).
373 | *
374 | * @return string New sort direction.
375 | */
376 | public function toggleSort($sort)
377 | {
378 | if ($sort == 'DESC') {
379 | return 'ASC';
380 | }
381 | if ($sort == 'ASC') {
382 | return 'DESC';
383 | }
384 |
385 | return $sort;
386 | }
387 | }
388 |
--------------------------------------------------------------------------------