CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 1.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 1.2
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Classes

  • AclBase
  • AclBehavior
  • AclComponent
  • AclNode
  • AclShell
  • Aco
  • AcoAction
  • AjaxHelper
  • ApcEngine
  • ApiShell
  • App
  • AppController
  • AppHelper
  • AppModel
  • Aro
  • AuthComponent
  • BakeShell
  • BehaviorCollection
  • Cache
  • CacheEngine
  • CacheHelper
  • CakeErrorController
  • CakeLog
  • CakeSchema
  • CakeSession
  • CakeSocket
  • ClassRegistry
  • Component
  • Configure
  • ConnectionManager
  • ConsoleShell
  • ContainableBehavior
  • Controller
  • ControllerTask
  • CookieComponent
  • DataSource
  • DbAcl
  • DbAclSchema
  • DbConfigTask
  • DboAdodb
  • DboDb2
  • DboFirebird
  • DboMssql
  • DboMysql
  • DboMysqlBase
  • DboMysqli
  • DboOdbc
  • DboOracle
  • DboPostgres
  • DboSource
  • DboSqlite
  • DboSybase
  • Debugger
  • EmailComponent
  • ErrorHandler
  • ExtractTask
  • File
  • FileEngine
  • Flay
  • Folder
  • FormHelper
  • Helper
  • HtmlHelper
  • HttpSocket
  • I18n
  • I18nModel
  • i18nSchema
  • I18nShell
  • Inflector
  • IniAcl
  • JavascriptHelper
  • JsHelper
  • JsHelperObject
  • L10n
  • MagicDb
  • MagicFileResource
  • MediaView
  • MemcacheEngine
  • Model
  • ModelBehavior
  • ModelTask
  • Multibyte
  • NumberHelper
  • Object
  • Overloadable
  • Overloadable2
  • PagesController
  • PaginatorHelper
  • Permission
  • PluginTask
  • ProjectTask
  • RequestHandlerComponent
  • Router
  • RssHelper
  • Sanitize
  • Scaffold
  • ScaffoldView
  • SchemaShell
  • Security
  • SecurityComponent
  • SessionComponent
  • SessionHelper
  • SessionsSchema
  • Set
  • Shell
  • String
  • TestSuiteShell
  • TestTask
  • TextHelper
  • ThemeView
  • TimeHelper
  • TranslateBehavior
  • TreeBehavior
  • Validation
  • View
  • ViewTask
  • XcacheEngine
  • Xml
  • XmlElement
  • XmlHelper
  • XmlManager
  • XmlNode
  • XmlTextNode

Functions

  • __enclose
  • make_clean_css
  • mb_encode_mimeheader
  • mb_stripos
  • mb_stristr
  • mb_strlen
  • mb_strpos
  • mb_strrchr
  • mb_strrichr
  • mb_strripos
  • mb_strrpos
  • mb_strstr
  • mb_strtolower
  • mb_strtoupper
  • mb_substr
  • mb_substr_count
  • write_css_cache
  1: <?php
  2: /* SVN FILE: $Id$ */
  3: /**
  4:  * Access Control List factory class.
  5:  *
  6:  * Permissions system.
  7:  *
  8:  * PHP versions 4 and 5
  9:  *
 10:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 11:  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 12:  *
 13:  * Licensed under The MIT License
 14:  * Redistributions of files must retain the above copyright notice.
 15:  *
 16:  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 17:  * @link          http://cakephp.org CakePHP(tm) Project
 18:  * @package       cake
 19:  * @subpackage    cake.cake.libs.controller.components
 20:  * @since         CakePHP(tm) v 0.10.0.1076
 21:  * @version       $Revision$
 22:  * @modifiedby    $LastChangedBy$
 23:  * @lastmodified  $Date$
 24:  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
 25:  */
 26: /**
 27:  * Access Control List factory class.
 28:  *
 29:  * Looks for ACL implementation class in core config, and returns an instance of that class.
 30:  *
 31:  * @package       cake
 32:  * @subpackage    cake.cake.libs.controller.components
 33:  */
 34: class AclComponent extends Object {
 35: /**
 36:  * Instance of an ACL class
 37:  *
 38:  * @var object
 39:  * @access protected
 40:  */
 41:     var $_Instance = null;
 42: /**
 43:  * Constructor. Will return an instance of the correct ACL class.
 44:  *
 45:  */
 46:     function __construct() {
 47:         $name = Inflector::camelize(strtolower(Configure::read('Acl.classname')));
 48:         if (!class_exists($name)) {
 49:             if (App::import('Component', $name)) {
 50:                 if (strpos($name, '.') !== false) {
 51:                     list($plugin, $name) = explode('.', $name);
 52:                 }
 53:                 $name .= 'Component';
 54:             } else {
 55:                 trigger_error(sprintf(__('Could not find %s.', true), $name), E_USER_WARNING);
 56:             }
 57:         }
 58:         $this->_Instance =& new $name();
 59:         $this->_Instance->initialize($this);
 60:     }
 61: /**
 62:  * Startup is not used
 63:  *
 64:  * @param object $controller Controller using this component
 65:  * @return boolean Proceed with component usage (true), or fail (false)
 66:  * @access public
 67:  */
 68:     function startup(&$controller) {
 69:         return true;
 70:     }
 71: /**
 72:  * Empty class defintion, to be overridden in subclasses.
 73:  *
 74:  * @access protected
 75:  */
 76:     function _initACL() {
 77:     }
 78: /**
 79:  * Pass-thru function for ACL check instance.
 80:  *
 81:  * @param string $aro ARO
 82:  * @param string $aco ACO
 83:  * @param string $action Action (defaults to *)
 84:  * @return boolean Success
 85:  * @access public
 86:  */
 87:     function check($aro, $aco, $action = "*") {
 88:         return $this->_Instance->check($aro, $aco, $action);
 89:     }
 90: /**
 91:  * Pass-thru function for ACL allow instance.
 92:  *
 93:  * @param string $aro ARO
 94:  * @param string $aco ACO
 95:  * @param string $action Action (defaults to *)
 96:  * @return boolean Success
 97:  * @access public
 98:  */
 99:     function allow($aro, $aco, $action = "*") {
100:         return $this->_Instance->allow($aro, $aco, $action);
101:     }
102: /**
103:  * Pass-thru function for ACL deny instance.
104:  *
105:  * @param string $aro ARO
106:  * @param string $aco ACO
107:  * @param string $action Action (defaults to *)
108:  * @return boolean Success
109:  * @access public
110:  */
111:     function deny($aro, $aco, $action = "*") {
112:         return $this->_Instance->deny($aro, $aco, $action);
113:     }
114: /**
115:  * Pass-thru function for ACL inherit instance.
116:  *
117:  * @param string $aro ARO
118:  * @param string $aco ACO
119:  * @param string $action Action (defaults to *)
120:  * @return boolean Success
121:  * @access public
122:  */
123:     function inherit($aro, $aco, $action = "*") {
124:         return $this->_Instance->inherit($aro, $aco, $action);
125:     }
126: /**
127:  * Pass-thru function for ACL grant instance.
128:  *
129:  * @param string $aro ARO
130:  * @param string $aco ACO
131:  * @param string $action Action (defaults to *)
132:  * @return boolean Success
133:  * @access public
134:  */
135:     function grant($aro, $aco, $action = "*") {
136:         return $this->_Instance->grant($aro, $aco, $action);
137:     }
138: /**
139:  * Pass-thru function for ACL grant instance.
140:  *
141:  * @param string $aro ARO
142:  * @param string $aco ACO
143:  * @param string $action Action (defaults to *)
144:  * @return boolean Success
145:  * @access public
146:  */
147:     function revoke($aro, $aco, $action = "*") {
148:         return $this->_Instance->revoke($aro, $aco, $action);
149:     }
150: }
151: /**
152:  * Access Control List abstract class. Not to be instantiated.
153:  * Subclasses of this class are used by AclComponent to perform ACL checks in Cake.
154:  *
155:  * @package       cake
156:  * @subpackage    cake.cake.libs.controller.components
157:  * @abstract
158:  */
159: class AclBase extends Object {
160: /**
161:  * This class should never be instantiated, just subclassed.
162:  *
163:  */
164:     function __construct() {
165:         if (strcasecmp(get_class($this), "AclBase") == 0 || !is_subclass_of($this, "AclBase")) {
166:             trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration.", true), E_USER_ERROR);
167:             return NULL;
168:         }
169:     }
170: /**
171:  * Empty method to be overridden in subclasses
172:  *
173:  * @param string $aro ARO
174:  * @param string $aco ACO
175:  * @param string $action Action (defaults to *)
176:  * @access public
177:  */
178:     function check($aro, $aco, $action = "*") {
179:     }
180: /**
181:  * Empty method to be overridden in subclasses
182:  *
183:  * @param object $component Component
184:  * @access public
185:  */
186:     function initialize(&$component) {
187:     }
188: }
189: /**
190:  * In this file you can extend the AclBase.
191:  *
192:  * @package       cake
193:  * @subpackage    cake.cake.libs.model
194:  */
195: class DbAcl extends AclBase {
196: /**
197:  * Constructor
198:  *
199:  */
200:     function __construct() {
201:         parent::__construct();
202:         if (!class_exists('AclNode')) {
203:             uses('model' . DS . 'db_acl');
204:         }
205:         $this->Aro =& ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro'));
206:         $this->Aco =& ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco'));
207:     }
208: /**
209:  * Enter description here...
210:  *
211:  * @param object $component
212:  * @return void
213:  * @access public
214:  */
215:     function initialize(&$component) {
216:         $component->Aro = $this->Aro;
217:         $component->Aco = $this->Aco;
218:     }
219: /**
220:  * Checks if the given $aro has access to action $action in $aco
221:  *
222:  * @param string $aro ARO
223:  * @param string $aco ACO
224:  * @param string $action Action (defaults to *)
225:  * @return boolean Success (true if ARO has access to action in ACO, false otherwise)
226:  * @access public
227:  */
228:     function check($aro, $aco, $action = "*") {
229:         if ($aro == null || $aco == null) {
230:             return false;
231:         }
232: 
233:         $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
234:         $aroPath = $this->Aro->node($aro);
235:         $acoPath = $this->Aco->node($aco);
236: 
237:         if (empty($aroPath) || empty($acoPath)) {
238:             trigger_error("DbAcl::check() - Failed ARO/ACO node lookup in permissions check.  Node references:\nAro: " . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
239:             return false;
240:         }
241: 
242:         if ($acoPath == null || $acoPath == array()) {
243:             trigger_error("DbAcl::check() - Failed ACO node lookup in permissions check.  Node references:\nAro: " . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
244:             return false;
245:         }
246: 
247:         $aroNode = $aroPath[0];
248:         $acoNode = $acoPath[0];
249: 
250:         if ($action != '*' && !in_array('_' . $action, $permKeys)) {
251:             trigger_error(sprintf(__("ACO permissions key %s does not exist in DbAcl::check()", true), $action), E_USER_NOTICE);
252:             return false;
253:         }
254: 
255:         $inherited = array();
256:         $acoIDs = Set::extract($acoPath, '{n}.' . $this->Aco->alias . '.id');
257: 
258:         $count = count($aroPath);
259:         for ($i = 0 ; $i < $count; $i++) {
260:             $permAlias = $this->Aro->Permission->alias;
261: 
262:             $perms = $this->Aro->Permission->find('all', array(
263:                 'conditions' => array(
264:                     "{$permAlias}.aro_id" => $aroPath[$i][$this->Aro->alias]['id'],
265:                     "{$permAlias}.aco_id" => $acoIDs
266:                 ),
267:                 'order' => array($this->Aco->alias . '.lft' => 'desc'),
268:                 'recursive' => 0
269:             ));
270: 
271:             if (empty($perms)) {
272:                 continue;
273:             } else {
274:                 $perms = Set::extract($perms, '{n}.' . $this->Aro->Permission->alias);
275:                 foreach ($perms as $perm) {
276:                     if ($action == '*') {
277: 
278:                         foreach ($permKeys as $key) {
279:                             if (!empty($perm)) {
280:                                 if ($perm[$key] == -1) {
281:                                     return false;
282:                                 } elseif ($perm[$key] == 1) {
283:                                     $inherited[$key] = 1;
284:                                 }
285:                             }
286:                         }
287: 
288:                         if (count($inherited) === count($permKeys)) {
289:                             return true;
290:                         }
291:                     } else {
292:                         switch ($perm['_' . $action]) {
293:                             case -1:
294:                                 return false;
295:                             case 0:
296:                                 continue;
297:                             break;
298:                             case 1:
299:                                 return true;
300:                             break;
301:                         }
302:                     }
303:                 }
304:             }
305:         }
306:         return false;
307:     }
308: /**
309:  * Allow $aro to have access to action $actions in $aco
310:  *
311:  * @param string $aro ARO
312:  * @param string $aco ACO
313:  * @param string $actions Action (defaults to *)
314:  * @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
315:  * @return boolean Success
316:  * @access public
317:  */
318:     function allow($aro, $aco, $actions = "*", $value = 1) {
319:         $perms = $this->getAclLink($aro, $aco);
320:         $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
321:         $save = array();
322: 
323:         if ($perms == false) {
324:             trigger_error(__('DbAcl::allow() - Invalid node', true), E_USER_WARNING);
325:             return false;
326:         }
327:         if (isset($perms[0])) {
328:             $save = $perms[0][$this->Aro->Permission->alias];
329:         }
330: 
331:         if ($actions == "*") {
332:             $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
333:             $save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
334:         } else {
335:             if (!is_array($actions)) {
336:                 $actions = array('_' . $actions);
337:             }
338:             if (is_array($actions)) {
339:                 foreach ($actions as $action) {
340:                     if ($action{0} != '_') {
341:                         $action = '_' . $action;
342:                     }
343:                     if (in_array($action, $permKeys)) {
344:                         $save[$action] = $value;
345:                     }
346:                 }
347:             }
348:         }
349:         list($save['aro_id'], $save['aco_id']) = array($perms['aro'], $perms['aco']);
350: 
351:         if ($perms['link'] != null && !empty($perms['link'])) {
352:             $save['id'] = $perms['link'][0][$this->Aro->Permission->alias]['id'];
353:         } else {
354:             unset($save['id']);
355:             $this->Aro->Permission->id = null;
356:         }
357:         return ($this->Aro->Permission->save($save) !== false);
358:     }
359: /**
360:  * Deny access for $aro to action $action in $aco
361:  *
362:  * @param string $aro ARO
363:  * @param string $aco ACO
364:  * @param string $actions Action (defaults to *)
365:  * @return boolean Success
366:  * @access public
367:  */
368:     function deny($aro, $aco, $action = "*") {
369:         return $this->allow($aro, $aco, $action, -1);
370:     }
371: /**
372:  * Let access for $aro to action $action in $aco be inherited
373:  *
374:  * @param string $aro ARO
375:  * @param string $aco ACO
376:  * @param string $actions Action (defaults to *)
377:  * @return boolean Success
378:  * @access public
379:  */
380:     function inherit($aro, $aco, $action = "*") {
381:         return $this->allow($aro, $aco, $action, 0);
382:     }
383: /**
384:  * Allow $aro to have access to action $actions in $aco
385:  *
386:  * @param string $aro ARO
387:  * @param string $aco ACO
388:  * @param string $actions Action (defaults to *)
389:  * @return boolean Success
390:  * @see allow()
391:  * @access public
392:  */
393:     function grant($aro, $aco, $action = "*") {
394:         return $this->allow($aro, $aco, $action);
395:     }
396: /**
397:  * Deny access for $aro to action $action in $aco
398:  *
399:  * @param string $aro ARO
400:  * @param string $aco ACO
401:  * @param string $actions Action (defaults to *)
402:  * @return boolean Success
403:  * @see deny()
404:  * @access public
405:  */
406:     function revoke($aro, $aco, $action = "*") {
407:         return $this->deny($aro, $aco, $action);
408:     }
409: /**
410:  * Get an array of access-control links between the given Aro and Aco
411:  *
412:  * @param string $aro ARO
413:  * @param string $aco ACO
414:  * @return array Indexed array with: 'aro', 'aco' and 'link'
415:  * @access public
416:  */
417:     function getAclLink($aro, $aco) {
418:         $obj = array();
419:         $obj['Aro'] = $this->Aro->node($aro);
420:         $obj['Aco'] = $this->Aco->node($aco);
421: 
422:         if (empty($obj['Aro']) || empty($obj['Aco'])) {
423:             return false;
424:         }
425: 
426:         return array(
427:             'aro' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
428:             'aco'  => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id'),
429:             'link' => $this->Aro->Permission->find('all', array('conditions' => array(
430:                 $this->Aro->Permission->alias . '.aro_id' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
431:                 $this->Aro->Permission->alias . '.aco_id' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id')
432:             )))
433:         );
434:     }
435: /**
436:  * Get the keys used in an ACO
437:  *
438:  * @param array $keys Permission model info
439:  * @return array ACO keys
440:  * @access protected
441:  */
442:     function _getAcoKeys($keys) {
443:         $newKeys = array();
444:         $keys = array_keys($keys);
445:         foreach ($keys as $key) {
446:             if (!in_array($key, array('id', 'aro_id', 'aco_id'))) {
447:                 $newKeys[] = $key;
448:             }
449:         }
450:         return $newKeys;
451:     }
452: }
453: /**
454:  * In this file you can extend the AclBase.
455:  *
456:  * @package       cake
457:  * @subpackage    cake.cake.libs.model.iniacl
458:  */
459: class IniAcl extends AclBase {
460: /**
461:  * Array with configuration, parsed from ini file
462:  *
463:  * @var array
464:  * @access public
465:  */
466:     var $config = null;
467: /**
468:  * The constructor must be overridden, as AclBase is abstract.
469:  *
470:  */
471:     function __construct() {
472:     }
473: /**
474:  * Main ACL check function. Checks to see if the ARO (access request object) has access to the ACO (access control object).
475:  * Looks at the acl.ini.php file for permissions (see instructions in /config/acl.ini.php).
476:  *
477:  * @param string $aro ARO
478:  * @param string $aco ACO
479:  * @param string $aco_action Action
480:  * @return boolean Success
481:  * @access public
482:  */
483:     function check($aro, $aco, $aco_action = null) {
484:         if ($this->config == null) {
485:             $this->config = $this->readConfigFile(CONFIGS . 'acl.ini.php');
486:         }
487:         $aclConfig = $this->config;
488: 
489:         if (isset($aclConfig[$aro]['deny'])) {
490:             $userDenies = $this->arrayTrim(explode(",", $aclConfig[$aro]['deny']));
491: 
492:             if (array_search($aco, $userDenies)) {
493:                 return false;
494:             }
495:         }
496: 
497:         if (isset($aclConfig[$aro]['allow'])) {
498:             $userAllows = $this->arrayTrim(explode(",", $aclConfig[$aro]['allow']));
499: 
500:             if (array_search($aco, $userAllows)) {
501:                 return true;
502:             }
503:         }
504: 
505:         if (isset($aclConfig[$aro]['groups'])) {
506:             $userGroups = $this->arrayTrim(explode(",", $aclConfig[$aro]['groups']));
507: 
508:             foreach ($userGroups as $group) {
509:                 if (array_key_exists($group, $aclConfig)) {
510:                     if (isset($aclConfig[$group]['deny'])) {
511:                         $groupDenies=$this->arrayTrim(explode(",", $aclConfig[$group]['deny']));
512: 
513:                         if (array_search($aco, $groupDenies)) {
514:                             return false;
515:                         }
516:                     }
517: 
518:                     if (isset($aclConfig[$group]['allow'])) {
519:                         $groupAllows = $this->arrayTrim(explode(",", $aclConfig[$group]['allow']));
520: 
521:                         if (array_search($aco, $groupAllows)) {
522:                             return true;
523:                         }
524:                     }
525:                 }
526:             }
527:         }
528:         return false;
529:     }
530: /**
531:  * Parses an INI file and returns an array that reflects the INI file's section structure. Double-quote friendly.
532:  *
533:  * @param string $fileName File
534:  * @return array INI section structure
535:  * @access public
536:  */
537:     function readConfigFile($fileName) {
538:         $fileLineArray = file($fileName);
539: 
540:         foreach ($fileLineArray as $fileLine) {
541:             $dataLine = trim($fileLine);
542:             $firstChar = substr($dataLine, 0, 1);
543: 
544:             if ($firstChar != ';' && $dataLine != '') {
545:                 if ($firstChar == '[' && substr($dataLine, -1, 1) == ']') {
546:                     $sectionName = preg_replace('/[\[\]]/', '', $dataLine);
547:                 } else {
548:                     $delimiter = strpos($dataLine, '=');
549: 
550:                     if ($delimiter > 0) {
551:                         $key = strtolower(trim(substr($dataLine, 0, $delimiter)));
552:                         $value = trim(substr($dataLine, $delimiter + 1));
553: 
554:                         if (substr($value, 0, 1) == '"' && substr($value, -1) == '"') {
555:                             $value = substr($value, 1, -1);
556:                         }
557: 
558:                         $iniSetting[$sectionName][$key]=stripcslashes($value);
559:                     } else {
560:                         if (!isset($sectionName)) {
561:                             $sectionName = '';
562:                         }
563: 
564:                         $iniSetting[$sectionName][strtolower(trim($dataLine))]='';
565:                     }
566:                 }
567:             }
568:         }
569: 
570:         return $iniSetting;
571:     }
572: /**
573:  * Removes trailing spaces on all array elements (to prepare for searching)
574:  *
575:  * @param array $array Array to trim
576:  * @return array Trimmed array
577:  * @access public
578:  */
579:     function arrayTrim($array) {
580:         foreach ($array as $key => $value) {
581:             $array[$key] = trim($value);
582:         }
583:         array_unshift($array, "");
584:         return $array;
585:     }
586: }
587: ?>
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs