' . $e->getTraceAsString() . ''; 52 | } 53 | -------------------------------------------------------------------------------- /public/js/utils.js: -------------------------------------------------------------------------------- 1 | let Profile = { 2 | check: function (id) { 3 | if ($.trim($("#" + id)[0].value) === '') { 4 | $("#" + id)[0].focus(); 5 | $("#" + id + "_alert").show(); 6 | 7 | return false; 8 | } 9 | 10 | return true; 11 | }, 12 | validate: function () { 13 | if (Profile.check("name") === false) { 14 | return false; 15 | } 16 | if (Profile.check("email") === false) { 17 | return false; 18 | } 19 | $("#profileForm")[0].submit(); 20 | } 21 | }; 22 | 23 | var SignUp = { 24 | check: function (id) { 25 | if ($.trim($("#" + id)[0].value) === '') { 26 | $("#" + id)[0].focus(); 27 | $("#" + id + "_alert").show(); 28 | 29 | return false; 30 | } 31 | 32 | return true; 33 | }, 34 | validate: function () { 35 | if (SignUp.check("name") === false) { 36 | return false; 37 | } 38 | if (SignUp.check("username") === false) { 39 | return false; 40 | } 41 | if (SignUp.check("email") === false) { 42 | return false; 43 | } 44 | if (SignUp.check("password") === false) { 45 | return false; 46 | } 47 | if ($("#password")[0].value !== $("#repeatPassword")[0].value) { 48 | $("#repeatPassword")[0].focus(); 49 | $("#repeatPassword_alert").show(); 50 | 51 | return false; 52 | } 53 | $("#registerForm")[0].submit(); 54 | } 55 | }; 56 | 57 | $(document).ready(function () { 58 | $("#registerForm .alert").hide(); 59 | $("#profileForm .alert").hide(); 60 | }); 61 | -------------------------------------------------------------------------------- /src/Constants/Status.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Constants; 15 | 16 | class Status 17 | { 18 | public const ACTIVE = 1; 19 | public const INACTIVE = 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/Controllers/AboutController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | class AboutController extends ControllerBase 17 | { 18 | public function initialize() 19 | { 20 | parent::initialize(); 21 | 22 | $this->tag->title() 23 | ->set('About us') 24 | ; 25 | } 26 | 27 | public function indexAction(): void 28 | { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Controllers/CompaniesController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Invo\Forms\CompaniesForm; 17 | use Invo\Models\Companies; 18 | use Phalcon\Mvc\Model\Criteria; 19 | use Phalcon\Paginator\Adapter\Model as Paginator; 20 | 21 | class CompaniesController extends ControllerBase 22 | { 23 | public function initialize() 24 | { 25 | parent::initialize(); 26 | 27 | $this->tag->title() 28 | ->set('Manage your companies') 29 | ; 30 | } 31 | 32 | /** 33 | * Shows the index action 34 | */ 35 | public function indexAction(): void 36 | { 37 | $this->view->form = new CompaniesForm(); 38 | } 39 | 40 | /** 41 | * Search companies based on current criteria 42 | */ 43 | public function searchAction(): void 44 | { 45 | if ($this->request->isPost()) { 46 | $query = Criteria::fromInput( 47 | $this->di, 48 | Companies::class, 49 | $this->request->getPost() 50 | ); 51 | 52 | $this->persistent->searchParams = ['di' => null] + $query->getParams(); 53 | } 54 | 55 | $parameters = []; 56 | if ($this->persistent->searchParams) { 57 | $parameters = $this->persistent->searchParams; 58 | } 59 | 60 | $companies = Companies::find($parameters); 61 | if (count($companies) == 0) { 62 | $this->flash->notice('The search did not find any companies'); 63 | 64 | $this->dispatcher->forward([ 65 | 'controller' => 'companies', 66 | 'action' => 'index', 67 | ]); 68 | 69 | return; 70 | } 71 | 72 | $paginator = new Paginator([ 73 | 'model' => Companies::class, 74 | 'parameters' => $parameters, 75 | 'limit' => 10, 76 | 'page' => $this->request->getQuery('page', 'int', 1), 77 | ]); 78 | 79 | $this->view->page = $paginator->paginate(); 80 | $this->view->companies = $companies; 81 | } 82 | 83 | /** 84 | * Shows the form to create a new company 85 | */ 86 | public function newAction(): void 87 | { 88 | $this->view->form = new CompaniesForm(null, ['edit' => true]); 89 | } 90 | 91 | /** 92 | * Edits a company based on its id 93 | * 94 | * @param int $id 95 | */ 96 | public function editAction($id): void 97 | { 98 | $company = Companies::findFirstById($id); 99 | if (!$company) { 100 | $this->flash->error('Company was not found'); 101 | 102 | $this->dispatcher->forward([ 103 | 'controller' => 'companies', 104 | 'action' => 'index', 105 | ]); 106 | 107 | return; 108 | } 109 | 110 | $this->view->form = new CompaniesForm($company, ['edit' => true]); 111 | } 112 | 113 | /** 114 | * Creates a new company 115 | */ 116 | public function createAction(): void 117 | { 118 | if (!$this->request->isPost()) { 119 | $this->dispatcher->forward([ 120 | 'controller' => 'companies', 121 | 'action' => 'index', 122 | ]); 123 | 124 | return; 125 | } 126 | 127 | $form = new CompaniesForm(); 128 | $company = new Companies(); 129 | 130 | $data = $this->request->getPost(); 131 | if (!$form->isValid($data, $company)) { 132 | foreach ($form->getMessages() as $message) { 133 | $this->flash->error((string) $message); 134 | } 135 | 136 | $this->dispatcher->forward([ 137 | 'controller' => 'companies', 138 | 'action' => 'new', 139 | ]); 140 | 141 | return; 142 | } 143 | 144 | if (!$company->save()) { 145 | foreach ($company->getMessages() as $message) { 146 | $this->flash->error((string) $message); 147 | } 148 | 149 | $this->dispatcher->forward([ 150 | 'controller' => 'companies', 151 | 'action' => 'new', 152 | ]); 153 | 154 | return; 155 | } 156 | 157 | $form->clear(); 158 | $this->flash->success('Company was created successfully'); 159 | 160 | $this->dispatcher->forward([ 161 | 'controller' => 'companies', 162 | 'action' => 'index', 163 | ]); 164 | } 165 | 166 | /** 167 | * Saves current company in screen 168 | */ 169 | public function saveAction(): void 170 | { 171 | if (!$this->request->isPost()) { 172 | $this->dispatcher->forward([ 173 | 'controller' => 'companies', 174 | 'action' => 'index', 175 | ]); 176 | 177 | return; 178 | } 179 | 180 | $id = $this->request->getPost('id', 'int'); 181 | $company = Companies::findFirstById($id); 182 | if (!$company) { 183 | $this->flash->error('Company does not exist'); 184 | 185 | $this->dispatcher->forward([ 186 | 'controller' => 'companies', 187 | 'action' => 'index', 188 | ]); 189 | 190 | return; 191 | } 192 | 193 | $data = $this->request->getPost(); 194 | $form = new CompaniesForm(); 195 | if (!$form->isValid($data, $company)) { 196 | foreach ($form->getMessages() as $message) { 197 | $this->flash->error((string) $message); 198 | } 199 | 200 | $this->dispatcher->forward([ 201 | 'controller' => 'companies', 202 | 'action' => 'new', 203 | ]); 204 | 205 | return; 206 | } 207 | 208 | if (!$company->save()) { 209 | foreach ($company->getMessages() as $message) { 210 | $this->flash->error((string) $message); 211 | } 212 | 213 | $this->dispatcher->forward([ 214 | 'controller' => 'companies', 215 | 'action' => 'new', 216 | ]); 217 | 218 | return; 219 | } 220 | 221 | $form->clear(); 222 | $this->flash->success('Company was updated successfully'); 223 | 224 | $this->dispatcher->forward([ 225 | 'controller' => 'companies', 226 | 'action' => 'index', 227 | ]); 228 | } 229 | 230 | /** 231 | * Deletes a company 232 | * 233 | * @param string $id 234 | */ 235 | public function deleteAction($id) 236 | { 237 | $companies = Companies::findFirstById($id); 238 | if (!$companies) { 239 | $this->flash->error('Company was not found'); 240 | 241 | $this->dispatcher->forward([ 242 | 'controller' => 'companies', 243 | 'action' => 'index', 244 | ]); 245 | 246 | return; 247 | } 248 | 249 | if (!$companies->delete()) { 250 | foreach ($companies->getMessages() as $message) { 251 | $this->flash->error((string) $message); 252 | } 253 | 254 | $this->dispatcher->forward([ 255 | 'controller' => 'companies', 256 | 'action' => 'search', 257 | ]); 258 | 259 | return; 260 | } 261 | 262 | $this->flash->success('Company was deleted'); 263 | 264 | $this->dispatcher->forward([ 265 | 'controller' => 'companies', 266 | 'action' => 'index', 267 | ]); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/Controllers/ContactController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Invo\Forms\ContactForm; 17 | use Invo\Models\Contact; 18 | 19 | /** 20 | * ContactController 21 | * 22 | * Allows to contact the staff using a contact form 23 | */ 24 | class ContactController extends ControllerBase 25 | { 26 | public function initialize() 27 | { 28 | parent::initialize(); 29 | 30 | $this->tag->title() 31 | ->set('Contact us') 32 | ; 33 | } 34 | 35 | public function indexAction(): void 36 | { 37 | $this->view->form = new ContactForm(); 38 | } 39 | 40 | /** 41 | * Saves the contact information in the database 42 | */ 43 | public function sendAction(): void 44 | { 45 | if (!$this->request->isPost()) { 46 | $this->dispatcher->forward([ 47 | 'controller' => 'contact', 48 | 'action' => 'index', 49 | ]); 50 | 51 | return; 52 | } 53 | 54 | $form = new ContactForm(); 55 | $contact = new Contact(); 56 | 57 | // Validate the form 58 | if (!$form->isValid($this->request->getPost(), $contact)) { 59 | foreach ($form->getMessages() as $message) { 60 | $this->flash->error((string) $message); 61 | } 62 | 63 | $this->dispatcher->forward([ 64 | 'controller' => 'contact', 65 | 'action' => 'index', 66 | ]); 67 | 68 | return; 69 | } 70 | 71 | if (!$contact->save()) { 72 | foreach ($contact->getMessages() as $message) { 73 | $this->flash->error((string) $message); 74 | } 75 | 76 | $this->dispatcher->forward([ 77 | 'controller' => 'contact', 78 | 'action' => 'index', 79 | ]); 80 | 81 | return; 82 | } 83 | 84 | $this->flash->success('Thanks, we will contact you in the next few hours'); 85 | 86 | $this->dispatcher->forward([ 87 | 'controller' => 'index', 88 | 'action' => 'index', 89 | ]); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Controllers/ControllerBase.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Phalcon\Mvc\Controller; 17 | 18 | class ControllerBase extends Controller 19 | { 20 | protected function initialize() 21 | { 22 | $this->tag->title() 23 | ->prepend('INVO | ') 24 | ; 25 | $this->view->setTemplateAfter('main'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Controllers/ErrorsController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | /** 17 | * ErrorsController 18 | * 19 | * Manage errors 20 | */ 21 | class ErrorsController extends ControllerBase 22 | { 23 | public function initialize() 24 | { 25 | $this->tag->title() 26 | ->set('Oops!') 27 | ; 28 | 29 | parent::initialize(); 30 | } 31 | 32 | public function show404Action(): void 33 | { 34 | $this->response->setStatusCode(404); 35 | } 36 | 37 | public function show401Action(): void 38 | { 39 | $this->response->setStatusCode(401); 40 | } 41 | 42 | public function show500Action(): void 43 | { 44 | $this->response->setStatusCode(500); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Controllers/IndexController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | class IndexController extends ControllerBase 17 | { 18 | public function initialize() 19 | { 20 | parent::initialize(); 21 | 22 | $this->tag->title() 23 | ->set('Welcome') 24 | ; 25 | } 26 | 27 | public function indexAction(): void 28 | { 29 | $this->flash->notice( 30 | 'This is a sample application of the Phalcon Framework. 31 | Please don\'t provide us any personal information. Thanks' 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Controllers/InvoicesController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Invo\Forms\ProfileForm; 17 | use Invo\Models\Users; 18 | 19 | /** 20 | * InvoicesController 21 | * 22 | * Manage operations for invoices 23 | */ 24 | class InvoicesController extends ControllerBase 25 | { 26 | public function initialize() 27 | { 28 | $this->tag->title() 29 | ->set('Manage your Invoices') 30 | ; 31 | 32 | parent::initialize(); 33 | } 34 | 35 | public function indexAction(): void 36 | { 37 | } 38 | 39 | /** 40 | * Edit the active user profile 41 | */ 42 | public function profileAction(): void 43 | { 44 | //Get session info 45 | $auth = $this->session->get('auth'); 46 | 47 | //Query the active user 48 | $user = Users::findFirst($auth['id']); 49 | if (!$user) { 50 | $this->dispatcher->forward([ 51 | 'controller' => 'index', 52 | 'action' => 'index', 53 | ]); 54 | 55 | return; 56 | } 57 | 58 | // Pass user to fill the form with the user data 59 | $form = new ProfileForm($user); 60 | 61 | if ($this->request->isPost()) { 62 | $data = $this->request->getPost(); 63 | 64 | if ($form->isValid($data, $user)) { 65 | if (!$user->save()) { 66 | foreach ($user->getMessages() as $message) { 67 | $this->flash->error((string) $message); 68 | } 69 | } else { 70 | $this->flash->success('Your profile information was updated successfully'); 71 | } 72 | } else { 73 | foreach ($form->getMessages() as $message) { 74 | $this->flash->error((string) $message); 75 | } 76 | } 77 | } 78 | 79 | $this->view->form = $form; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Controllers/ProductsController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Invo\Constants\Status; 17 | use Invo\Forms\ProductsForm; 18 | use Invo\Models\Products; 19 | use Phalcon\Mvc\Model\Criteria; 20 | use Phalcon\Paginator\Adapter\Model as Paginator; 21 | 22 | /** 23 | * ProductsController 24 | * 25 | * Manage CRUD operations for products 26 | */ 27 | class ProductsController extends ControllerBase 28 | { 29 | public function initialize() 30 | { 31 | parent::initialize(); 32 | 33 | $this->tag->title() 34 | ->set('Manage your products') 35 | ; 36 | } 37 | 38 | /** 39 | * Shows the index action 40 | */ 41 | public function indexAction(): void 42 | { 43 | $this->view->form = new ProductsForm(); 44 | } 45 | 46 | /** 47 | * Search products based on current criteria 48 | */ 49 | public function searchAction(): void 50 | { 51 | if ($this->request->isPost()) { 52 | $query = Criteria::fromInput( 53 | $this->di, 54 | Products::class, 55 | $this->request->getPost() 56 | ); 57 | 58 | $this->persistent->searchParams = ['di' => null] + $query->getParams(); 59 | } 60 | 61 | $parameters = []; 62 | if ($this->persistent->searchParams) { 63 | $parameters = $this->persistent->searchParams; 64 | } 65 | 66 | $products = Products::find($parameters); 67 | if (count($products) == 0) { 68 | $this->flash->notice('The search did not find any products'); 69 | 70 | $this->dispatcher->forward([ 71 | 'controller' => 'products', 72 | 'action' => 'index', 73 | ]); 74 | 75 | return; 76 | } 77 | 78 | $paginator = new Paginator([ 79 | 'model' => Products::class, 80 | 'parameters' => $parameters, 81 | 'limit' => 10, 82 | 'page' => $this->request->getQuery('page', 'int', 1), 83 | ]); 84 | 85 | $this->view->page = $paginator->paginate(); 86 | } 87 | 88 | /** 89 | * Shows the form to create a new product 90 | */ 91 | public function newAction(): void 92 | { 93 | $this->view->form = new ProductsForm(null, ['edit' => true]); 94 | } 95 | 96 | /** 97 | * Edits a product based on its id 98 | * 99 | * @param $id 100 | */ 101 | public function editAction($id): void 102 | { 103 | $product = Products::findFirstById($id); 104 | if (!$product) { 105 | $this->flash->error('Product was not found'); 106 | 107 | $this->dispatcher->forward([ 108 | 'controller' => 'products', 109 | 'action' => 'index', 110 | ]); 111 | 112 | return; 113 | } 114 | 115 | $this->view->form = new ProductsForm($product, ['edit' => true]); 116 | } 117 | 118 | /** 119 | * Creates a new product 120 | */ 121 | public function createAction(): void 122 | { 123 | if (!$this->request->isPost()) { 124 | $this->dispatcher->forward([ 125 | 'controller' => 'products', 126 | 'action' => 'index', 127 | ]); 128 | 129 | return; 130 | } 131 | 132 | $form = new ProductsForm(); 133 | $product = new Products(); 134 | $product->active = Status::ACTIVE; 135 | 136 | if (!$form->isValid($this->request->getPost(), $product)) { 137 | foreach ($form->getMessages() as $message) { 138 | $this->flash->error($message); 139 | } 140 | 141 | $this->dispatcher->forward([ 142 | 'controller' => 'products', 143 | 'action' => 'new', 144 | ]); 145 | 146 | return; 147 | } 148 | 149 | if (!$product->save()) { 150 | foreach ($product->getMessages() as $message) { 151 | $this->flash->error((string) $message); 152 | } 153 | 154 | $this->dispatcher->forward([ 155 | 'controller' => 'products', 156 | 'action' => 'new', 157 | ]); 158 | 159 | return; 160 | } 161 | 162 | $form->clear(); 163 | $this->flash->success('Product was created successfully'); 164 | 165 | $this->dispatcher->forward([ 166 | 'controller' => 'products', 167 | 'action' => 'index', 168 | ]); 169 | } 170 | 171 | /** 172 | * Saves current product in screen 173 | */ 174 | public function saveAction(): void 175 | { 176 | if (!$this->request->isPost()) { 177 | $this->dispatcher->forward([ 178 | 'controller' => 'products', 179 | 'action' => 'index', 180 | ]); 181 | 182 | return; 183 | } 184 | 185 | $id = $this->request->getPost('id', 'int'); 186 | $product = Products::findFirstById($id); 187 | if (!$product) { 188 | $this->flash->error('Product does not exist'); 189 | 190 | $this->dispatcher->forward([ 191 | 'controller' => 'products', 192 | 'action' => 'index', 193 | ]); 194 | 195 | return; 196 | } 197 | 198 | $form = new ProductsForm(); 199 | $this->view->form = $form; 200 | $data = $this->request->getPost(); 201 | 202 | if (!$form->isValid($data, $product)) { 203 | foreach ($form->getMessages() as $message) { 204 | $this->flash->error($message); 205 | } 206 | 207 | $this->dispatcher->forward([ 208 | 'controller' => 'products', 209 | 'action' => 'edit', 210 | 'params' => [$id], 211 | ]); 212 | 213 | return; 214 | } 215 | 216 | if (!$product->save()) { 217 | foreach ($product->getMessages() as $message) { 218 | $this->flash->error($message); 219 | } 220 | 221 | $this->dispatcher->forward([ 222 | 'controller' => 'products', 223 | 'action' => 'edit', 224 | 'params' => [$id], 225 | ]); 226 | 227 | return; 228 | } 229 | 230 | $form->clear(); 231 | $this->flash->success('Product was updated successfully'); 232 | 233 | $this->dispatcher->forward([ 234 | 'controller' => 'products', 235 | 'action' => 'index', 236 | ]); 237 | } 238 | 239 | /** 240 | * Deletes a product 241 | * 242 | * @param string $id 243 | */ 244 | public function deleteAction($id): void 245 | { 246 | $products = Products::findFirstById($id); 247 | if (!$products) { 248 | $this->flash->error('Product was not found'); 249 | 250 | $this->dispatcher->forward([ 251 | 'controller' => 'products', 252 | 'action' => 'index', 253 | ]); 254 | 255 | return; 256 | } 257 | 258 | if (!$products->delete()) { 259 | foreach ($products->getMessages() as $message) { 260 | $this->flash->error($message); 261 | } 262 | 263 | $this->dispatcher->forward([ 264 | 'controller' => 'products', 265 | 'action' => 'search', 266 | ]); 267 | 268 | return; 269 | } 270 | 271 | $this->flash->success('Product was deleted'); 272 | 273 | $this->dispatcher->forward([ 274 | 'controller' => 'products', 275 | 'action' => 'index', 276 | ]); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/Controllers/ProducttypesController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Invo\Forms\ProductTypesForm; 17 | use Invo\Models\ProductTypes; 18 | use Phalcon\Mvc\Model\Criteria; 19 | use Phalcon\Paginator\Adapter\Model as Paginator; 20 | 21 | /** 22 | * ProductTypesController 23 | * 24 | * Manage operations for product of types 25 | */ 26 | class ProducttypesController extends ControllerBase 27 | { 28 | public function initialize() 29 | { 30 | $this->tag->title() 31 | ->set('Manage your products types') 32 | ; 33 | 34 | parent::initialize(); 35 | } 36 | 37 | /** 38 | * Shows the index action 39 | */ 40 | public function indexAction(): void 41 | { 42 | $this->view->form = new ProductTypesForm(); 43 | } 44 | 45 | /** 46 | * Search producttype based on current criteria 47 | */ 48 | public function searchAction(): void 49 | { 50 | if ($this->request->isPost()) { 51 | $query = Criteria::fromInput( 52 | $this->di, 53 | ProductTypes::class, 54 | $this->request->getPost() 55 | ); 56 | 57 | $this->persistent->searchParams = ['di' => null] + $query->getParams(); 58 | } 59 | 60 | $parameters = []; 61 | if ($this->persistent->searchParams) { 62 | $parameters = $this->persistent->searchParams; 63 | } 64 | 65 | $productTypes = ProductTypes::find($parameters); 66 | if (count($productTypes) === 0) { 67 | $this->flash->notice('The search did not find any product types'); 68 | 69 | $this->dispatcher->forward([ 70 | 'controller' => 'producttypes', 71 | 'action' => 'index', 72 | ]); 73 | 74 | return; 75 | } 76 | 77 | $paginator = new Paginator([ 78 | 'model' => ProductTypes::class, 79 | 'parameters' => $parameters, 80 | 'limit' => 10, 81 | 'page' => $this->request->getQuery('page', 'int', 1), 82 | ]); 83 | 84 | $this->view->page = $paginator->paginate(); 85 | $this->view->productTypes = $productTypes; 86 | } 87 | 88 | /** 89 | * Shows the form to create a new producttype 90 | */ 91 | public function newAction(): void 92 | { 93 | $this->view->form = new ProductTypesForm(null, ['edit' => true]); 94 | } 95 | 96 | /** 97 | * Edits a producttype based on its id 98 | * 99 | * @param int $id 100 | */ 101 | public function editAction($id): void 102 | { 103 | $productTypes = ProductTypes::findFirstById($id); 104 | if (!$productTypes) { 105 | $this->flash->error('Product type to edit was not found'); 106 | 107 | $this->dispatcher->forward([ 108 | 'controller' => 'producttypes', 109 | 'action' => 'index', 110 | ]); 111 | 112 | return; 113 | } 114 | 115 | $this->view->form = new ProductTypesForm($productTypes, ['edit' => true]); 116 | } 117 | 118 | /** 119 | * Creates a new producttype 120 | */ 121 | public function createAction(): void 122 | { 123 | if (!$this->request->isPost()) { 124 | $this->dispatcher->forward([ 125 | 'controller' => 'producttypes', 126 | 'action' => 'index', 127 | ]); 128 | 129 | return; 130 | } 131 | 132 | $form = new ProductTypesForm(); 133 | $productTypes = new ProductTypes(); 134 | 135 | $data = $this->request->getPost(); 136 | if (!$form->isValid($data, $productTypes)) { 137 | foreach ($form->getMessages() as $message) { 138 | $this->flash->error((string) $message); 139 | } 140 | 141 | $this->dispatcher->forward([ 142 | 'controller' => 'producttypes', 143 | 'action' => 'new', 144 | ]); 145 | 146 | return; 147 | } 148 | 149 | if (!$productTypes->save()) { 150 | foreach ($productTypes->getMessages() as $message) { 151 | $this->flash->error((string) $message); 152 | } 153 | 154 | $this->dispatcher->forward([ 155 | 'controller' => 'producttypes', 156 | 'action' => 'new', 157 | ]); 158 | 159 | return; 160 | } 161 | 162 | $form->clear(); 163 | $this->flash->success('Product type was created successfully'); 164 | 165 | $this->dispatcher->forward([ 166 | 'controller' => 'producttypes', 167 | 'action' => 'index', 168 | ]); 169 | } 170 | 171 | /** 172 | * Saves current producttypes in screen 173 | */ 174 | public function saveAction(): void 175 | { 176 | if (!$this->request->isPost()) { 177 | $this->dispatcher->forward([ 178 | 'controller' => 'producttypes', 179 | 'action' => 'index', 180 | ]); 181 | 182 | return; 183 | } 184 | 185 | $id = $this->request->getPost('id', 'int'); 186 | $productTypes = ProductTypes::findFirstById($id); 187 | if (!$productTypes) { 188 | $this->flash->error('productTypes does not exist'); 189 | 190 | $this->dispatcher->forward([ 191 | 'controller' => 'producttypes', 192 | 'action' => 'index', 193 | ]); 194 | 195 | return; 196 | } 197 | 198 | $form = new ProductTypesForm(); 199 | if (!$form->isValid($this->request->getPost(), $productTypes)) { 200 | foreach ($form->getMessages() as $message) { 201 | $this->flash->error($message); 202 | } 203 | 204 | $this->dispatcher->forward([ 205 | 'controller' => 'producttypes', 206 | 'action' => 'new', 207 | ]); 208 | 209 | return; 210 | } 211 | 212 | if (!$productTypes->save()) { 213 | foreach ($productTypes->getMessages() as $message) { 214 | $this->flash->error($message); 215 | } 216 | 217 | $this->dispatcher->forward([ 218 | 'controller' => 'producttypes', 219 | 'action' => 'new', 220 | ]); 221 | 222 | return; 223 | } 224 | 225 | $form->clear(); 226 | $this->flash->success('Product Type was updated successfully'); 227 | 228 | $this->dispatcher->forward([ 229 | 'controller' => 'producttypes', 230 | 'action' => 'index', 231 | ]); 232 | } 233 | 234 | /** 235 | * Deletes a producttypes 236 | * 237 | * @param int $id 238 | */ 239 | public function deleteAction($id): void 240 | { 241 | $productTypes = ProductTypes::findFirstById($id); 242 | if (!$productTypes) { 243 | $this->flash->error('Product types was not found'); 244 | 245 | $this->dispatcher->forward([ 246 | 'controller' => 'producttypes', 247 | 'action' => 'index', 248 | ]); 249 | 250 | return; 251 | } 252 | 253 | if (!$productTypes->delete()) { 254 | foreach ($productTypes->getMessages() as $message) { 255 | $this->flash->error((string) $message); 256 | } 257 | 258 | $this->dispatcher->forward([ 259 | 'controller' => 'producttypes', 260 | 'action' => 'search', 261 | ]); 262 | 263 | return; 264 | } 265 | 266 | $this->flash->success('Product types was deleted'); 267 | 268 | $this->dispatcher->forward([ 269 | 'controller' => 'producttypes', 270 | 'action' => 'index', 271 | ]); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/Controllers/RegisterController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Invo\Constants\Status; 17 | use Invo\Forms\RegisterForm; 18 | use Invo\Models\Users; 19 | use Phalcon\Db\RawValue; 20 | 21 | /** 22 | * SessionController 23 | * 24 | * Allows to register new users 25 | */ 26 | class RegisterController extends ControllerBase 27 | { 28 | public function initialize() 29 | { 30 | $this->tag->title() 31 | ->set('Sign Up/Sign In') 32 | ; 33 | 34 | parent::initialize(); 35 | } 36 | 37 | /** 38 | * Action to register a new user 39 | */ 40 | public function indexAction(): void 41 | { 42 | $form = new RegisterForm(); 43 | 44 | if ($this->request->isPost()) { 45 | $newUser = new Users(); 46 | if ($form->isValid($this->request->getPost(), $newUser)) { 47 | $newUser->password = sha1($form->getFilteredValue('password')); 48 | $newUser->created_at = new RawValue('now()'); 49 | $newUser->active = Status::ACTIVE; 50 | 51 | if ($newUser->save()) { 52 | $this->flash->success( 53 | 'Thanks for sign-up, please log-in to start generating invoices' 54 | ); 55 | 56 | $this->dispatcher->forward([ 57 | 'controller' => 'session', 58 | 'action' => 'index', 59 | ]); 60 | 61 | return; 62 | } 63 | } else { 64 | foreach ($form->getMessages() as $message) { 65 | $this->flash->error((string) $message); 66 | } 67 | } 68 | } 69 | 70 | $this->view->form = $form; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Controllers/SessionController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Controllers; 15 | 16 | use Invo\Constants\Status; 17 | use Invo\Forms\LoginForm; 18 | use Invo\Models\Users; 19 | 20 | /** 21 | * SessionController 22 | * 23 | * Allows to authenticate users 24 | */ 25 | class SessionController extends ControllerBase 26 | { 27 | public function initialize() 28 | { 29 | parent::initialize(); 30 | 31 | $this->tag->title() 32 | ->set('Sign Up/Sign In') 33 | ; 34 | } 35 | 36 | public function indexAction(): void 37 | { 38 | $form = new LoginForm(); 39 | 40 | // Set default Invo user credentials 41 | $form->get('email')->setDefault('demo'); 42 | $form->get('password')->setDefault('phalcon'); 43 | 44 | $this->view->form = $form; 45 | } 46 | 47 | /** 48 | * This action authenticate and logs an user into the application 49 | */ 50 | public function startAction(): void 51 | { 52 | if ($this->request->isPost()) { 53 | $email = $this->request->getPost('email'); 54 | $password = $this->request->getPost('password'); 55 | 56 | /** @var Users $user */ 57 | $user = Users::findFirst( 58 | [ 59 | "conditions" => "(email = :email: OR username = :email:) " 60 | . "AND password = :password: " 61 | . "AND active = :active:", 62 | 'bind' => [ 63 | 'email' => $email, 64 | 'password' => sha1($password), 65 | 'active' => Status::ACTIVE, 66 | ], 67 | ] 68 | ); 69 | 70 | if ($user) { 71 | $this->registerSession($user); 72 | $this->flash->success('Welcome ' . $user->name); 73 | 74 | $this->dispatcher->forward([ 75 | 'controller' => 'invoices', 76 | 'action' => 'index', 77 | ]); 78 | 79 | return; 80 | } 81 | 82 | $this->flash->error('Wrong email/password'); 83 | } 84 | 85 | $this->dispatcher->forward([ 86 | 'controller' => 'session', 87 | 'action' => 'index', 88 | ]); 89 | } 90 | 91 | /** 92 | * Finishes the active session redirecting to the index 93 | */ 94 | public function endAction(): void 95 | { 96 | $this->session->remove('auth'); 97 | $this->flash->success('Goodbye!'); 98 | 99 | $this->dispatcher->forward([ 100 | 'controller' => 'index', 101 | 'action' => 'index', 102 | ]); 103 | } 104 | 105 | /** 106 | * Register an authenticated user into session data 107 | * 108 | * @param Users $user 109 | */ 110 | private function registerSession(Users $user): void 111 | { 112 | $this->session->set('auth', [ 113 | 'id' => $user->id, 114 | 'name' => $user->name, 115 | ]); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Forms/CompaniesForm.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Forms; 15 | 16 | use Phalcon\Filter\Validation\Validator\PresenceOf; 17 | use Phalcon\Forms\Element\Hidden; 18 | use Phalcon\Forms\Element\Text; 19 | use Phalcon\Forms\Form; 20 | 21 | class CompaniesForm extends Form 22 | { 23 | /** 24 | * Initialize the companies form 25 | * 26 | * @param null $entity 27 | * @param array $options 28 | */ 29 | public function initialize($entity = null, array $options = []) 30 | { 31 | if (!isset($options['edit'])) { 32 | $this->add((new Text('id'))->setLabel('Id')); 33 | } else { 34 | $this->add(new Hidden('id')); 35 | } 36 | 37 | $commonFilters = [ 38 | 'striptags', 39 | 'string', 40 | ]; 41 | 42 | /** 43 | * Name text field 44 | */ 45 | $name = new Text('name'); 46 | $name->setLabel('Name'); 47 | $name->setFilters($commonFilters); 48 | $name->addValidators([ 49 | new PresenceOf(['message' => 'Name is required']), 50 | ]); 51 | 52 | $this->add($name); 53 | 54 | /** 55 | * Telephone text field 56 | */ 57 | $telephone = new Text('telephone'); 58 | $telephone->setLabel('Telephone'); 59 | $telephone->setFilters($commonFilters); 60 | $telephone->addValidators([ 61 | new PresenceOf(['message' => 'Telephone is required']), 62 | ]); 63 | 64 | $this->add($telephone); 65 | 66 | /** 67 | * Address text field 68 | */ 69 | $address = new Text('address'); 70 | $address->setLabel('address'); 71 | $address->setFilters($commonFilters); 72 | $address->addValidators([ 73 | new PresenceOf(['message' => 'Address is required']), 74 | ]); 75 | 76 | $this->add($address); 77 | 78 | /** 79 | * City text field 80 | */ 81 | $city = new Text('city'); 82 | $city->setLabel('city'); 83 | $city->setFilters($commonFilters); 84 | $city->addValidators([ 85 | new PresenceOf(['message' => 'City is required']), 86 | ]); 87 | 88 | $this->add($city); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Forms/ContactForm.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Forms; 15 | 16 | use Phalcon\Filter\Validation\Validator\Email; 17 | use Phalcon\Filter\Validation\Validator\PresenceOf; 18 | use Phalcon\Forms\Element\Text; 19 | use Phalcon\Forms\Element\TextArea; 20 | use Phalcon\Forms\Form; 21 | 22 | class ContactForm extends Form 23 | { 24 | /** 25 | * @param null $entity 26 | * @param array $options 27 | */ 28 | public function initialize($entity = null, array $options = []) 29 | { 30 | /** 31 | * Name text field 32 | */ 33 | $name = new Text('name'); 34 | $name->setLabel('Your Full Name'); 35 | $name->setFilters(['striptags', 'string']); 36 | $name->addValidators([ 37 | new PresenceOf(['message' => 'Name is required']), 38 | ]); 39 | 40 | $this->add($name); 41 | 42 | /** 43 | * Email field 44 | */ 45 | $email = new Text('email'); 46 | $email->setLabel('E-Mail'); 47 | $email->setFilters('email'); 48 | $email->addValidators([ 49 | new PresenceOf(['message' => 'E-mail is required']), 50 | new Email(['message' => 'E-mail is not valid']) 51 | ]); 52 | 53 | $this->add($email); 54 | 55 | /** 56 | * Comment textarea 57 | */ 58 | $comments = new TextArea('comments'); 59 | $comments->setLabel('Comments'); 60 | $comments->setFilters(['striptags', 'string']); 61 | $comments->addValidators([ 62 | new PresenceOf(['message' => 'Comments is required']), 63 | ]); 64 | 65 | $this->add($comments); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Forms/LoginForm.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Forms; 15 | 16 | use Phalcon\Filter\Validation\Validator\PresenceOf; 17 | use Phalcon\Forms\Element\Password; 18 | use Phalcon\Forms\Element\Text; 19 | use Phalcon\Forms\Form; 20 | 21 | class LoginForm extends Form 22 | { 23 | /** 24 | */ 25 | public function initialize() 26 | { 27 | /** 28 | * Username/Email text field 29 | */ 30 | $email = new Text('email'); 31 | $email->setLabel('Username/Email'); 32 | $email->setFilters(['striptags', 'string']); 33 | $email->addValidators([ 34 | new PresenceOf(['message' => 'Username/Email is required']), 35 | ]); 36 | 37 | $this->add($email); 38 | 39 | /** 40 | * Password field 41 | */ 42 | $password = new Password('password'); 43 | $password->setLabel('Password'); 44 | $password->addValidators([ 45 | new PresenceOf(['message' => 'Password is required']), 46 | ]); 47 | 48 | $this->add($password); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Forms/ProductTypesForm.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Forms; 15 | 16 | use Phalcon\Filter\Validation\Validator\PresenceOf; 17 | use Phalcon\Forms\Element\Hidden; 18 | use Phalcon\Forms\Element\Text; 19 | use Phalcon\Forms\Form; 20 | 21 | class ProductTypesForm extends Form 22 | { 23 | /** 24 | * Initialize the products form 25 | * 26 | * @param null $entity 27 | * @param array $options 28 | */ 29 | public function initialize($entity = null, array $options = []) 30 | { 31 | if (!isset($options['edit'])) { 32 | $this->add((new Text('id'))->setLabel('Id')); 33 | } else { 34 | $this->add(new Hidden('id')); 35 | } 36 | 37 | $name = new Text('name'); 38 | $name->setLabel('Name'); 39 | $name->setFilters(['striptags', 'string']); 40 | $name->addValidators([ 41 | new PresenceOf(['message' => 'Name is required']), 42 | ]); 43 | 44 | $this->add($name); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Forms/ProductsForm.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Forms; 15 | 16 | use Invo\Models\ProductTypes; 17 | use Phalcon\Filter\Validation\Validator\Numericality; 18 | use Phalcon\Filter\Validation\Validator\PresenceOf; 19 | use Phalcon\Forms\Element\Hidden; 20 | use Phalcon\Forms\Element\Select; 21 | use Phalcon\Forms\Element\Text; 22 | use Phalcon\Forms\Form; 23 | 24 | class ProductsForm extends Form 25 | { 26 | /** 27 | * Initialize the products form 28 | * 29 | * @param null $entity 30 | * @param array $options 31 | */ 32 | public function initialize($entity = null, array $options = []) 33 | { 34 | if (!isset($options['edit'])) { 35 | $this->add((new Text('id'))->setLabel('Id')); 36 | } else { 37 | $this->add(new Hidden('id')); 38 | } 39 | 40 | /** 41 | * Name text field 42 | */ 43 | $name = new Text('name'); 44 | $name->setLabel('Name'); 45 | $name->setFilters(['striptags', 'string']); 46 | $name->addValidators([ 47 | new PresenceOf(['message' => 'Name is required']), 48 | ]); 49 | 50 | $this->add($name); 51 | 52 | /** 53 | * Product Type Id Select 54 | */ 55 | $type = new Select( 56 | 'product_types_id', 57 | ProductTypes::find(), 58 | [ 59 | 'using' => ['id', 'name'], 60 | 'useEmpty' => true, 61 | 'emptyText' => '...', 62 | 'emptyValue' => '', 63 | ] 64 | ); 65 | $type->setLabel('Type'); 66 | 67 | $this->add($type); 68 | 69 | /** 70 | * Price text field 71 | */ 72 | $price = new Text('price'); 73 | $price->setLabel('Price'); 74 | $price->setFilters(['float']); 75 | $price->addValidators([ 76 | new PresenceOf(['message' => 'Price is required']), 77 | new Numericality(['message' => 'Price is required']), 78 | ]); 79 | 80 | $this->add($price); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Forms/ProfileForm.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Forms; 15 | 16 | use Phalcon\Filter\Validation\Validator\Email; 17 | use Phalcon\Filter\Validation\Validator\PresenceOf; 18 | use Phalcon\Filter\Validation\Validator\Uniqueness as UniquenessValidator; 19 | use Phalcon\Forms\Element\Text; 20 | use Phalcon\Forms\Form; 21 | 22 | class ProfileForm extends Form 23 | { 24 | public function initialize() 25 | { 26 | /** 27 | * Name text field 28 | */ 29 | $name = new Text('name'); 30 | $name->setLabel('Your Full Name'); 31 | $name->setFilters(['striptags', 'string']); 32 | $name->addValidators([ 33 | new PresenceOf(['message' => 'Name is required']), 34 | ]); 35 | 36 | $this->add($name); 37 | 38 | /** 39 | * Email text field 40 | */ 41 | $email = new Text('email'); 42 | $email->setLabel('E-Mail Address'); 43 | $email->setFilters('email'); 44 | $email->addValidators([ 45 | new PresenceOf(['message' => 'E-mail is required']), 46 | new Email(['message' => 'E-mail is not valid']), 47 | new UniquenessValidator( 48 | [ 49 | 'message' => 'Sorry, The email was registered by another user', 50 | ] 51 | ) 52 | ]); 53 | 54 | $this->add($email); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Forms/RegisterForm.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Forms; 15 | 16 | use Phalcon\Filter\Validation\Validator\Email; 17 | use Phalcon\Filter\Validation\Validator\PresenceOf; 18 | use Phalcon\Filter\Validation\Validator\Uniqueness as UniquenessValidator; 19 | use Phalcon\Filter\Validation\Validator\Confirmation; 20 | use Phalcon\Filter\Validation\Validator\StringLength\Min; 21 | use Phalcon\Forms\Element\Password; 22 | use Phalcon\Forms\Element\Text; 23 | use Phalcon\Forms\Form; 24 | 25 | class RegisterForm extends Form 26 | { 27 | /** 28 | * @param null $entity 29 | * @param null $options 30 | */ 31 | public function initialize($entity = null, $options = null) 32 | { 33 | /** 34 | * Name text field 35 | */ 36 | $name = new Text('name'); 37 | $name->setLabel('Your Full Name'); 38 | $name->setFilters(['striptags', 'string']); 39 | $name->addValidators([ 40 | new PresenceOf(['message' => 'Name is required']), 41 | ]); 42 | 43 | $this->add($name); 44 | 45 | /** 46 | * Username text field 47 | */ 48 | $name = new Text('username'); 49 | $name->setLabel('Username'); 50 | $name->setFilters(['alnum']); 51 | $name->addValidators([ 52 | new PresenceOf(['message' => 'Please enter your desired user name']), 53 | new UniquenessValidator( 54 | [ 55 | 'message' => 'Sorry, That username is already taken', 56 | ] 57 | ) 58 | ]); 59 | 60 | $this->add($name); 61 | 62 | /** 63 | * Email text field 64 | */ 65 | $email = new Text('email'); 66 | $email->setLabel('E-Mail'); 67 | $email->setFilters('email'); 68 | $email->addValidators([ 69 | new PresenceOf(['message' => 'E-mail is required']), 70 | new Email(['message' => 'E-mail is not valid']), 71 | new UniquenessValidator( 72 | [ 73 | 'message' => 'Sorry, The email was registered by another user', 74 | ] 75 | ) 76 | ]); 77 | 78 | $this->add($email); 79 | 80 | /** 81 | * Password field 82 | */ 83 | $password = new Password('password'); 84 | $password->setLabel('Password'); 85 | $password->addValidators([ 86 | new PresenceOf(['message' => 'Password is required']), 87 | new Min(['min' => 8, 'message' => 'Password must be at least 8 characters']), 88 | ]); 89 | 90 | $this->add($password); 91 | 92 | /** 93 | * Confirm Password field 94 | */ 95 | $repeatPassword = new Password('repeatPassword'); 96 | $repeatPassword->setLabel('Repeat Password'); 97 | $repeatPassword->addValidators([ 98 | new PresenceOf(['message' => 'Confirmation password is required']), 99 | new Confirmation(["message" => "Passwords are different", "with" => "password",]), 100 | ]); 101 | 102 | $this->add($repeatPassword); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Models/Companies.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Models; 15 | 16 | use Phalcon\Mvc\Model; 17 | 18 | class Companies extends Model 19 | { 20 | /** 21 | * @var integer 22 | */ 23 | public $id; 24 | 25 | /** 26 | * @var string 27 | */ 28 | public $name; 29 | 30 | /** 31 | * @var string 32 | */ 33 | public $telephone; 34 | 35 | /** 36 | * @var string 37 | */ 38 | public $address; 39 | 40 | /** 41 | * @var string 42 | */ 43 | public $city; 44 | } 45 | -------------------------------------------------------------------------------- /src/Models/Contact.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Models; 15 | 16 | use Phalcon\Db\RawValue; 17 | use Phalcon\Mvc\Model; 18 | 19 | class Contact extends Model 20 | { 21 | public $id; 22 | 23 | public $name; 24 | 25 | public $email; 26 | 27 | public $comments; 28 | 29 | public $created_at; 30 | 31 | public function beforeCreate() 32 | { 33 | $this->created_at = new RawValue('now()'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Models/ProductTypes.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Models; 15 | 16 | use Phalcon\Mvc\Model; 17 | 18 | /** 19 | * Types of Products 20 | */ 21 | class ProductTypes extends Model 22 | { 23 | /** 24 | * @var integer 25 | */ 26 | public $id; 27 | 28 | /** 29 | * @var string 30 | */ 31 | public $name; 32 | 33 | /** 34 | * ProductTypes initializer 35 | */ 36 | public function initialize() 37 | { 38 | $this->hasMany( 39 | 'id', 40 | Products::class, 41 | 'product_types_id', 42 | [ 43 | 'foreignKey' => [ 44 | 'message' => 'Product Type cannot be deleted because it\'s used in Products' 45 | ], 46 | ] 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Models/Products.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Models; 15 | 16 | use Invo\Constants\Status; 17 | use Phalcon\Mvc\Model; 18 | 19 | /** 20 | * Products 21 | * @property ProductTypes $productType 22 | */ 23 | class Products extends Model 24 | { 25 | /** 26 | * @var integer 27 | */ 28 | public $id; 29 | 30 | /** 31 | * @var integer 32 | */ 33 | public $product_types_id; 34 | 35 | /** 36 | * @var string 37 | */ 38 | public $name; 39 | 40 | /** 41 | * @var string 42 | */ 43 | public $price; 44 | 45 | /** 46 | * @var string 47 | */ 48 | public $active; 49 | 50 | /** 51 | * Products initializer 52 | */ 53 | public function initialize() 54 | { 55 | $this->belongsTo( 56 | 'product_types_id', 57 | ProductTypes::class, 58 | 'id', 59 | [ 60 | 'reusable' => true, 61 | 'alias' => 'productTypes', 62 | ] 63 | ); 64 | } 65 | 66 | /** 67 | * Returns a human representation of 'active' 68 | * 69 | * @return string 70 | */ 71 | public function getActiveDetail(): string 72 | { 73 | return $this->active == Status::ACTIVE ? 'Yes' : 'No'; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Models/Users.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Models; 15 | 16 | use Phalcon\Db\RawValue; 17 | use Phalcon\Mvc\Model; 18 | 19 | class Users extends Model 20 | { 21 | /** 22 | * @var integer|null 23 | */ 24 | public ?int $id = null; 25 | 26 | /** 27 | * @var string 28 | */ 29 | public string $username; 30 | 31 | /** 32 | * @var string 33 | */ 34 | public string $password; 35 | 36 | /** 37 | * @var string 38 | */ 39 | public string $name; 40 | 41 | /** 42 | * @var string 43 | */ 44 | public string $email; 45 | 46 | /** 47 | * @var string|RawValue 48 | */ 49 | public string|RawValue $created_at; 50 | 51 | /** 52 | * @var integer 53 | */ 54 | public int $active; 55 | } 56 | -------------------------------------------------------------------------------- /src/Plugins/NotFoundPlugin.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Plugins; 15 | 16 | use Exception; 17 | use Phalcon\Di\Injectable; 18 | use Phalcon\Events\Event; 19 | use Phalcon\Mvc\Dispatcher as MvcDispatcher; 20 | use Phalcon\Mvc\Dispatcher\Exception as DispatcherException; 21 | 22 | /** 23 | * NotFoundPlugin 24 | * 25 | * Handles not-found controller/actions 26 | */ 27 | class NotFoundPlugin extends Injectable 28 | { 29 | /** 30 | * This action is executed before perform any action in the application 31 | * 32 | * @param Event $event 33 | * @param MvcDispatcher $dispatcher 34 | * @param Exception $exception 35 | * 36 | * @return bool 37 | */ 38 | public function beforeException(Event $event, MvcDispatcher $dispatcher, Exception $exception) 39 | { 40 | error_log($exception->getMessage() . PHP_EOL . $exception->getTraceAsString()); 41 | 42 | if ($exception instanceof DispatcherException) { 43 | switch ($exception->getCode()) { 44 | case DispatcherException::EXCEPTION_HANDLER_NOT_FOUND: 45 | case DispatcherException::EXCEPTION_ACTION_NOT_FOUND: 46 | $dispatcher->forward([ 47 | 'controller' => 'errors', 48 | 'action' => 'show404', 49 | ]); 50 | 51 | return false; 52 | } 53 | } 54 | 55 | if ($dispatcher->getControllerName() !== 'errors') { 56 | $dispatcher->forward([ 57 | 'controller' => 'errors', 58 | 'action' => 'show500', 59 | ]); 60 | } 61 | 62 | return !$event->isStopped(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Plugins/SecurityPlugin.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Plugins; 15 | 16 | use Phalcon\Acl\Adapter\Memory as AclList; 17 | use Phalcon\Acl\Component; 18 | use Phalcon\Acl\Enum; 19 | use Phalcon\Acl\Role; 20 | use Phalcon\Di\Injectable; 21 | use Phalcon\Events\Event; 22 | use Phalcon\Mvc\Dispatcher; 23 | 24 | /** 25 | * SecurityPlugin 26 | * 27 | * This is the security plugin which controls that users only have access to the modules they're assigned to 28 | */ 29 | class SecurityPlugin extends Injectable 30 | { 31 | /** 32 | * This action is executed before execute any action in the application 33 | * 34 | * @param Event $event 35 | * @param Dispatcher $dispatcher 36 | * 37 | * @return bool 38 | */ 39 | public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) 40 | { 41 | $auth = $this->session->get('auth'); 42 | if (!$auth) { 43 | $role = 'Guests'; 44 | } else { 45 | $role = 'Users'; 46 | } 47 | 48 | $controller = $dispatcher->getControllerName(); 49 | $action = $dispatcher->getActionName(); 50 | 51 | $acl = $this->getAcl(); 52 | 53 | if (!$acl->isComponent($controller)) { 54 | $dispatcher->forward([ 55 | 'controller' => 'errors', 56 | 'action' => 'show404', 57 | ]); 58 | 59 | return false; 60 | } 61 | 62 | $allowed = $acl->isAllowed($role, $controller, $action); 63 | if (!$allowed) { 64 | $dispatcher->forward([ 65 | 'controller' => 'errors', 66 | 'action' => 'show401', 67 | ]); 68 | 69 | $this->session->destroy(); 70 | 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | 77 | /** 78 | * Returns an existing or new access control list 79 | * 80 | * @returns AclList 81 | */ 82 | protected function getAcl(): AclList 83 | { 84 | if (isset($this->persistent->acl)) { 85 | return $this->persistent->acl; 86 | } 87 | 88 | $acl = new AclList(); 89 | $acl->setDefaultAction(Enum::DENY); 90 | 91 | // Register roles 92 | $roles = [ 93 | 'users' => new Role( 94 | 'Users', 95 | 'Member privileges, granted after sign in.' 96 | ), 97 | 'guests' => new Role( 98 | 'Guests', 99 | 'Anyone browsing the site who is not signed in is considered to be a "Guest".' 100 | ) 101 | ]; 102 | 103 | foreach ($roles as $role) { 104 | $acl->addRole($role); 105 | } 106 | 107 | //Private area resources 108 | $privateResources = [ 109 | 'companies' => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'], 110 | 'products' => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'], 111 | 'producttypes' => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'], 112 | 'invoices' => ['index', 'profile'], 113 | ]; 114 | foreach ($privateResources as $resource => $actions) { 115 | $acl->addComponent(new Component($resource), $actions); 116 | } 117 | 118 | //Public area resources 119 | $publicResources = [ 120 | 'index' => ['index'], 121 | 'about' => ['index'], 122 | 'register' => ['index'], 123 | 'errors' => ['show401', 'show404', 'show500'], 124 | 'session' => ['index', 'register', 'start', 'end'], 125 | 'contact' => ['index', 'send'], 126 | ]; 127 | foreach ($publicResources as $resource => $actions) { 128 | $acl->addComponent(new Component($resource), $actions); 129 | } 130 | 131 | //Grant access to public areas to both users and guests 132 | foreach ($roles as $role) { 133 | foreach ($publicResources as $resource => $actions) { 134 | foreach ($actions as $action) { 135 | $acl->allow($role->getName(), $resource, $action); 136 | } 137 | } 138 | } 139 | 140 | //Grant access to private area to role Users 141 | foreach ($privateResources as $resource => $actions) { 142 | foreach ($actions as $action) { 143 | $acl->allow('Users', $resource, $action); 144 | } 145 | } 146 | 147 | //The acl is stored in session, APC would be useful here too 148 | $this->persistent->acl = $acl; 149 | 150 | return $acl; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/Providers/ConfigProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Exception; 17 | use Phalcon\Di\DiInterface; 18 | use Phalcon\Di\ServiceProviderInterface; 19 | 20 | /** 21 | * Read the configuration 22 | */ 23 | class ConfigProvider implements ServiceProviderInterface 24 | { 25 | public function register(DiInterface $di): void 26 | { 27 | $configPath = $di->offsetGet('rootPath') . '/config/config.php'; 28 | if (!file_exists($configPath) || !is_readable($configPath)) { 29 | throw new Exception('Config file does not exist: ' . $configPath); 30 | } 31 | 32 | $di->setShared('config', function () use ($configPath) { 33 | return require_once $configPath; 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Providers/DatabaseProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Phalcon\Di\DiInterface; 17 | use Phalcon\Di\ServiceProviderInterface; 18 | 19 | use function var_dump; 20 | 21 | /** 22 | * Database connection is created based in the parameters defined in the configuration file 23 | */ 24 | class DatabaseProvider implements ServiceProviderInterface 25 | { 26 | public function register(DiInterface $di): void 27 | { 28 | $dbConfig = $di->getShared('config') 29 | ->get('database') 30 | ->toArray() 31 | ; 32 | $di->setShared('db', function () use ($dbConfig) { 33 | $dbClass = 'Phalcon\Db\Adapter\Pdo\\' . $dbConfig['adapter']; 34 | unset($dbConfig['adapter']); 35 | 36 | return new $dbClass($dbConfig); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Providers/DispatcherProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Invo\Plugins\NotFoundPlugin; 17 | use Invo\Plugins\SecurityPlugin; 18 | use Phalcon\Di\DiInterface; 19 | use Phalcon\Di\ServiceProviderInterface; 20 | use Phalcon\Events\Manager as EventsManager; 21 | use Phalcon\Mvc\Dispatcher; 22 | 23 | /** 24 | * We register the events manager 25 | */ 26 | class DispatcherProvider implements ServiceProviderInterface 27 | { 28 | public function register(DiInterface $di): void 29 | { 30 | $di->setShared('dispatcher', function () { 31 | $eventsManager = new EventsManager(); 32 | 33 | /** 34 | * Check if the user is allowed to access certain action using the SecurityPlugin 35 | */ 36 | $eventsManager->attach('dispatch:beforeExecuteRoute', new SecurityPlugin()); 37 | 38 | /** 39 | * Handle exceptions and not-found exceptions using NotFoundPlugin 40 | */ 41 | $eventsManager->attach('dispatch:beforeException', new NotFoundPlugin()); 42 | 43 | $dispatcher = new Dispatcher(); 44 | $dispatcher->setDefaultNamespace('Invo\Controllers'); 45 | $dispatcher->setEventsManager($eventsManager); 46 | 47 | return $dispatcher; 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Providers/FlashProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Phalcon\Di\DiInterface; 17 | use Phalcon\Di\ServiceProviderInterface; 18 | use Phalcon\Flash\Direct as FlashDirect; 19 | 20 | /** 21 | * Register the flash service with custom CSS classes 22 | */ 23 | class FlashProvider implements ServiceProviderInterface 24 | { 25 | public function register(DiInterface $di): void 26 | { 27 | $di->setShared('flash', function () { 28 | $flash = new FlashDirect(); 29 | $flash->setImplicitFlush(false); 30 | $flash->setCssClasses([ 31 | 'error' => 'alert alert-danger', 32 | 'success' => 'alert alert-success', 33 | 'notice' => 'alert alert-info', 34 | 'warning' => 'alert alert-warning' 35 | ]); 36 | 37 | return $flash; 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Providers/SessionBagProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Phalcon\Di\DiInterface; 17 | use Phalcon\Di\ServiceProviderInterface; 18 | use Phalcon\Session\Bag; 19 | 20 | class SessionBagProvider implements ServiceProviderInterface 21 | { 22 | public function register(DiInterface $di): void 23 | { 24 | $session = $di->getShared('session'); 25 | $di->setShared( 26 | 'sessionBag', 27 | function () use ($session) { 28 | return new Bag($session, 'bag'); 29 | } 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Providers/SessionProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Phalcon\Di\DiInterface; 17 | use Phalcon\Di\ServiceProviderInterface; 18 | use Phalcon\Session\Adapter\Stream as SessionAdapter; 19 | use Phalcon\Session\Manager as SessionManager; 20 | 21 | /** 22 | * Start the session the first time some component request the session service 23 | */ 24 | class SessionProvider implements ServiceProviderInterface 25 | { 26 | public function register(DiInterface $di): void 27 | { 28 | $di->setShared('session', function () { 29 | $session = new SessionManager(); 30 | $files = new SessionAdapter([ 31 | 'savePath' => sys_get_temp_dir(), 32 | ]); 33 | $session->setAdapter($files); 34 | $session->start(); 35 | 36 | return $session; 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Providers/UrlProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Phalcon\Di\DiInterface; 17 | use Phalcon\Di\ServiceProviderInterface; 18 | use Phalcon\Mvc\Url; 19 | 20 | /** 21 | * The URL component is used to generate all kind of urls in the application 22 | */ 23 | class UrlProvider implements ServiceProviderInterface 24 | { 25 | public function register(DiInterface $di): void 26 | { 27 | $baseUri = $di->getShared('config')->application->baseUri; 28 | $di->setShared('url', function () use ($baseUri) { 29 | $url = new Url(); 30 | $url->setBaseUri($baseUri); 31 | 32 | return $url; 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Providers/ViewProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Phalcon\Di\DiInterface; 17 | use Phalcon\Di\ServiceProviderInterface; 18 | use Phalcon\Mvc\View; 19 | 20 | class ViewProvider implements ServiceProviderInterface 21 | { 22 | public function register(DiInterface $di): void 23 | { 24 | $viewsDir = $di->get('rootPath') . DIRECTORY_SEPARATOR . $di->getShared('config')->application->viewsDir; 25 | 26 | $di->setShared('view', function () use ($viewsDir) { 27 | $view = new View(); 28 | $view->setViewsDir($viewsDir); 29 | $view->registerEngines([ 30 | '.volt' => 'volt' 31 | ]); 32 | 33 | return $view; 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Providers/VoltProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view 9 | * the LICENSE file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Invo\Providers; 15 | 16 | use Phalcon\Di\DiInterface; 17 | use Phalcon\Di\ServiceProviderInterface; 18 | use Phalcon\Mvc\View\Engine\Volt as VoltEngine; 19 | 20 | class VoltProvider implements ServiceProviderInterface 21 | { 22 | public function register(DiInterface $di): void 23 | { 24 | $view = $di->getShared('view'); 25 | 26 | $di->setShared('volt', function () use ($view, $di) { 27 | $volt = new VoltEngine($view, $di); 28 | $volt->setOptions([ 29 | 'path' => $di->offsetGet('rootPath') . '/var/cache/volt/', 30 | ]); 31 | 32 | $compiler = $volt->getCompiler(); 33 | $compiler->addFunction('is_a', 'is_a'); 34 | 35 | return $volt; 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | amOnPage('/about'); 14 | $I->see('About INVO'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/acceptance/Controllers/ErrorsControllerCest.php: -------------------------------------------------------------------------------- 1 | amOnPage('/test'); 14 | $I->see('Page not found'); 15 | $I->seeResponseCodeIs(404); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/acceptance/Controllers/IndexControllerCest.php: -------------------------------------------------------------------------------- 1 | amOnPage('/'); 14 | $I->see('Welcome to INVO'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/functional.suite.yml: -------------------------------------------------------------------------------- 1 | # Codeception Test Suite Configuration 2 | # 3 | # Suite for functional tests 4 | # Emulate web requests and make application process them 5 | # Include one of framework modules (Symfony2, Yii2, Laravel5) to use it 6 | # Remove this suite if you don't use frameworks 7 | 8 | actor: FunctionalTester 9 | modules: 10 | enabled: 11 | # add a framework module here 12 | - \Helper\Functional 13 | step_decorators: ~ -------------------------------------------------------------------------------- /tests/functional/Forms/ProductTypesFormTest.php: -------------------------------------------------------------------------------- 1 | 'string'], true], 25 | [[$key => '
6 | This is a sample application for the Phalcon Framework. 7 | We expect to implement as many features as possible to show how the framework works and its potential. 8 | Please write us if you have any feedback or comments. 9 | Feel free to clone the code of this application here. 10 | Thanks! 11 |
12 | -------------------------------------------------------------------------------- /themes/invo/companies/edit.volt: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /themes/invo/companies/index.volt: -------------------------------------------------------------------------------- 1 |Id | 16 |Name | 17 |Telephone | 18 |Address | 19 |City | 20 |||
---|---|---|---|---|---|---|
{{ company.id }} | 26 |{{ company.name }} | 27 |{{ company.telephone }} | 28 |{{ company.address }} | 29 |{{ company.city }} | 30 |{{ tag.a(url('companies/edit/' ~ company.id), " Edit", ['class': 'btn btn-default'], true) }} | 31 |{{ tag.a(url('companies/delete/' ~ company.id), " Delete", ['class': 'btn btn-default'], true) }} | 32 |
36 |
37 | {{ tag.a(url('companies/search'), " First", ['class': 'btn btn-default'], true) }}
38 | {{ tag.a(url('companies/search?page=') ~ page.getPrevious(), " Previous", ['class': 'btn btn-default'], true) }}
39 | {{ tag.a(url('companies/search?page=') ~ page.getNext(), " Next", ['class': 'btn btn-default'], true) }}
40 | {{ tag.a(url('companies/search?page=') ~ page.getLast(), " Last", ['class': 'btn btn-default'], true) }}
41 | {{ page.getCurrent() }}/{{ page.getLast() }}
42 |
43 | |
44 |
Send us a message and let us know how we can help. Please be as descriptive as possible as it will help us serve you better.
6 | 7 | 26 | -------------------------------------------------------------------------------- /themes/invo/errors/show401.volt: -------------------------------------------------------------------------------- 1 |You don't have access to this option. Contact an administrator
4 |{{ tag.a(url('index'), 'Home', ['class':'btn btn-primary']) }}
5 |Sorry, you have accessed a page that does not exist or was moved
4 |{{ tag.a(url('index'), 'Home', ['class':'btn btn-primary']) }}
5 |Something went wrong, if the error continue please contact us
4 |{{ tag.a(url('index'), 'Home', ['class':'btn btn-primary']) }}
5 |INVO is a revolutionary application to create invoices online for free. 4 | Receive online payments from your clients and improve your cash flow
5 |{{ tag.a(url('register'), 'Try it for Free »', ['class':'btn btn-primary btn-large btn-success'], true) }}
6 |Create, track and export your invoices online. Automate recurring invoices and design your own invoice using our invoice template and brand it with your business logo.
12 |Gain critical insights into how your business is doing. See what sells most, who are your top paying customers and the average time your customers take to pay.
16 |Invite users and share your workload as invoice supports multiple users with different permissions. It helps your business to be more productive and efficient.
20 |Number | 7 |Customer | 8 |Date | 9 |Total | 10 |Status | 11 |
51001 | 16 |Friðrik Þór Friðriksson | 17 |2014-04-02 | 18 |12.50 | 19 |Success | 20 |
51002 | 23 |Keith Carradine | 24 |2014-04-04 | 25 |22.75 | 26 |Rejected | 27 |
51003 | 30 |Nico Engelbrecht | 31 |2014-04-04 | 32 |6.50 | 33 |Success | 34 |
51004 | 37 |Clinton Kayser | 38 |2014-04-07 | 39 |11.50 | 40 |Success | 41 |
Id | 16 |Product Type | 17 |Name | 18 |Price | 19 |Active | 20 |||
---|---|---|---|---|---|---|
{{ product.id }} | 26 |{{ product.productTypes.name }} | 27 |{{ product.name }} | 28 |${{ "%.2f"|format(product.price) }} | 29 |{{ product.getActiveDetail() }} | 30 |{{ tag.a(url('products/edit/' ~ product.id), " Edit", ['class': 'btn btn-default'], true) }} | 31 |{{ tag.a(url('products/delete/' ~ product.id), " Delete", ['class': 'btn btn-default'], true) }} | 32 |
36 |
37 | {{ tag.a(url('products/search'), " First", ['class': 'btn'], true) }}
38 | {{ tag.a(url('products/search?page=') ~ page.getPrevious(), " Previous", ['class': 'btn'], true) }}
39 | {{ tag.a(url('products/search?page=') ~ page.getNext(), " Next", ['class': 'btn'], true) }}
40 | {{ tag.a(url('products/search?page=') ~ page.getLast(), " Last", ['class': 'btn'], true) }}
41 | {{ page.getCurrent() }} of {{ page.getLast() }}
42 |
43 | |
44 |
Id | 16 |Name | 17 |||
---|---|---|---|
{{ producttype.id }} | 23 |{{ producttype.name }} | 24 |{{ tag.a(url('producttypes/edit/' ~ producttype.id), " Edit", ['class': 'btn btn-default'], true) }} | 25 |{{ tag.a(url('producttypes/delete/' ~ producttype.id), " Delete", ['class': 'btn btn-default'], true) }} | 26 |
30 |
31 | {{ tag.a(url('producttypes/search'), " First", ['class': 'btn'], true) }}
32 | {{ tag.a(url('producttypes/search?page=') ~ page.getPrevious(), " Previous", ['class': 'btn'], true) }}
33 | {{ tag.a(url('producttypes/search?page=') ~ page.getNext(), " Next", ['class': 'btn'], true) }}
34 | {{ tag.a(url('producttypes/search?page=') ~ page.getLast(), " Last", ['class': 'btn'], true) }}
35 | {{ page.getCurrent() }}/{{ page.getLast() }}
36 |
37 | |
38 |
Create an account offers the following advantages:
34 |