├── .gitignore ├── src ├── Rapyd │ ├── Views │ │ ├── Macros.html.twig │ │ ├── Pagination.twig │ │ ├── DataGrid.twig │ │ ├── Error.twig │ │ └── Form.layout.twig │ ├── Model.php │ ├── Config │ │ └── routes.php │ ├── Widgets │ │ ├── WidgetBuilder.php │ │ ├── DataGrid.php │ │ ├── DataGrid │ │ │ └── Column.php │ │ ├── DataSet.php │ │ ├── DataForm.php │ │ └── Widget.php │ ├── Error.php │ ├── Helpers │ │ ├── Parser.php │ │ ├── Lang.php │ │ ├── Pagination.php │ │ ├── HTML.php │ │ └── Url.php │ ├── Event.php │ ├── Controller.php │ └── Application.php ├── Modules │ ├── Users │ │ ├── Views │ │ │ └── Count.twig │ │ ├── Models │ │ │ └── User.php │ │ ├── Config │ │ │ └── routes.php │ │ └── Controllers │ │ │ └── Users.php │ └── Demos │ │ ├── Controllers │ │ ├── Demo.php │ │ ├── Hello.php │ │ ├── Eloquent.php │ │ ├── Dataset.php │ │ ├── Tests.php │ │ ├── Datagrid.php │ │ ├── Forms.php │ │ └── Schema.php │ │ ├── Views │ │ ├── Grid.twig │ │ ├── Hello.twig │ │ ├── Schema.twig │ │ ├── Form.twig │ │ ├── Eloquent.twig │ │ ├── Set.twig │ │ ├── Index.twig │ │ └── Demo.layout.twig │ │ ├── Models │ │ ├── User.php │ │ ├── Comment.php │ │ └── Article.php │ │ └── Config │ │ └── routes.php └── App │ ├── Config │ ├── twig.php │ ├── routes.php │ ├── db.php │ ├── config.php │ └── hooks.php │ ├── Views │ ├── Home.twig │ ├── 404.twig │ ├── layout.twig │ └── error.twig │ └── Controllers │ └── Home.php ├── web ├── favicon.ico ├── .htaccess └── index.php ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /composer.lock 2 | /vendor/ -------------------------------------------------------------------------------- /src/Rapyd/Views/Macros.html.twig: -------------------------------------------------------------------------------- 1 | 2 | {# TODO #} 3 | -------------------------------------------------------------------------------- /src/Modules/Users/Views/Count.twig: -------------------------------------------------------------------------------- 1 | 2 | Totale: {{ count }} 3 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zofe/rapyd-framework/HEAD/web/favicon.ico -------------------------------------------------------------------------------- /web/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteRule ^ index.php [QSA,L] -------------------------------------------------------------------------------- /src/Rapyd/Model.php: -------------------------------------------------------------------------------- 1 | '(/pag/\d+)?(/orderby/\w+/(asc|desc))?(/pag/\d+)?' 5 | )); 6 | -------------------------------------------------------------------------------- /src/Modules/Users/Models/User.php: -------------------------------------------------------------------------------- 1 | addRoutes(array( 6 | '/users' => $users . ':index', 7 | '/users/count' => $users . ':count', 8 | )); 9 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Demo.php: -------------------------------------------------------------------------------- 1 | render('Index'); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/App/Config/twig.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'charset' => 'utf-8', 6 | //'cache' => realpath('../templates/cache'), 7 | 'auto_reload' => true, 8 | 'strict_variables' => false, 9 | 'autoescape' => true 10 | ); 11 | 12 | return $conf; 13 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Hello.php: -------------------------------------------------------------------------------- 1 | render('Hello', array('somevar'=>'Hello World!')); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/App/Config/routes.php: -------------------------------------------------------------------------------- 1 | addRoutes(array( 4 | '/' => 'Home:index', 5 | '/hello/:name' => 'Home:hello', 6 | '/test/qs' => 'Home:qs', 7 | '/test/ds' => 'Home:dataset', 8 | '/test/form' => 'Home:form', 9 | '/test/schema' => 'Home:schema', 10 | )); 11 | -------------------------------------------------------------------------------- /src/App/Config/db.php: -------------------------------------------------------------------------------- 1 | 'mysql', 6 | 'host' => '127.0.0.1', 7 | 'database' => 'rapyd_framework', 8 | 'username' => 'root', 9 | 'password' => '', 10 | 'charset' => 'utf8', 11 | 'collation' => 'utf8_unicode_ci', 12 | 'prefix' => '', 13 | ); 14 | 15 | return $conf; 16 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/WidgetBuilder.php: -------------------------------------------------------------------------------- 1 | widget = $classname; 13 | } 14 | 15 | public function createBuilder() 16 | { 17 | return new $this->widget; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Rapyd/Error.php: -------------------------------------------------------------------------------- 1 | app->environment(); 11 | $env['slim.errors'] = fopen('/path/to/output', 'w'); 12 | 13 | // Call next middleware 14 | $this->next->call(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Grid.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Grid.twig #} 3 | 4 | {% extends 'Demo.layout.twig' %} 5 | 6 | 7 | {% block title %} DataGrid {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {{ dg|raw }} 12 | 13 | {% endblock %} 14 | 15 | 16 | {% block code %} 17 |
{{ somevar }}
13 | 14 | {% endblock %} 15 | 16 | {% block code %} 17 |Congratulations! Rapyd is setup and working correclty.
15 | 16 | Getting started 17 |
15 | {{ article.body }}
16 | by {{ article.author.firstname }}
17 |
| 12 | {% spaceless %} 13 | {% if column.orderby %} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {{ column.label }} 22 | {% else %} 23 | {{ column.label }} 24 | {% endif %} 25 | {% endspaceless %} 26 | | 27 | {% endfor %} 28 |
|---|
| {{ cell.value|raw }} | 34 | {% endfor %} 35 |
36 | {{ error }} 37 |
38 | 39 |40 | Visit the Home Page 41 |
42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zofe/rapyd-framework", 3 | "type": "project", 4 | "description": "microframework for PHP 5.3 CRUD pattern", 5 | "homepage": "http://www.rapyd.com", 6 | "keywords": ["Rapyd", "Slim", "Eloquent", "Twig", "CRUD"], 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Felice Ostuni", 11 | "email": "felice.ostuni@gmail.com", 12 | "homepage": "http://www.feliceostuni.com", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.3.0", 18 | "slim/slim": "2.*", 19 | "slim/views": "0.1.*", 20 | "twig/twig": "1.*", 21 | "illuminate/database": "4.0.*", 22 | "symfony/form": "2.3.*", 23 | "symfony/validator": "2.3.*", 24 | "symfony/config": "2.3.*", 25 | "symfony/translation": "2.3.*", 26 | "symfony/twig-bridge": "2.3.*" 27 | }, 28 | "minimum-stability": "dev", 29 | "autoload": { 30 | "psr-0": { 31 | "App": "src/", 32 | "Rapyd": "src/", 33 | "Modules": "src/" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Rapyd/Helpers/Parser.php: -------------------------------------------------------------------------------- 1 | env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); 13 | 14 | //aggiungere una estensione o filtri custom per abilitare callback da controller/querybuilder/model/viste?? 15 | //studiare http://twig.sensiolabs.org/doc/advanced.html 16 | //es: $dg->column(' {{ name }} ',"NAME") 17 | //$this->env->addExtension($extension); 18 | } 19 | 20 | ////es: $dg->column('{{ name }}, {{ lastname }}',"NAME") 21 | public function render($pattern, $array) 22 | { 23 | return $this->env->render($pattern,$array); 24 | } 25 | 26 | public function variables($pattern) 27 | { 28 | if (preg_match_all("/\{\{ (\w+)(\|\w+)? \}\}/U", $pattern, $m)) { 29 | 30 | //$m = array_map('array_filter', $m); // Remove empty values 31 | array_shift($m); // Remove first index [0] 32 | 33 | return $m[0]; 34 | } 35 | 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Set.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Set.twig #} 3 | 4 | {% extends 'Demo.layout.twig' %} 5 | 6 | 7 | {% block title %} DataSet {% endblock %} 8 | 9 | 10 | {% block content %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for article in ds.data %} 22 | 23 |
26 | {{ article.body }}
27 | by {{ article.author.firstname }}
28 |
37 | The page you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly. If all else fails, you can visit our home page at the link below.
38 |
39 |
40 | Visit the Home Page
41 |
39 | Message: {{ e.message }}
40 | {% if e.file %}Filename: {{ e.file }}
{% endif %}
41 | Line Number: {{ e.line }}
42 |
48 | Backtrace:
49 |
50 | {% for error in trace %}
51 | {% if error.file %}
52 | Line: {{ error.line }} - {{ error.function }} - File: {{ error.file }}
53 | {% endif %}
54 | {% endfor %}
55 |
56 | {% endif %}
57 |
58 | 59 | Visit the Home Page 60 |
61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/App/Config/hooks.php: -------------------------------------------------------------------------------- 1 | hook('slim.before', function () use ($app) { 5 | $env = $app->environment(); 6 | 7 | //var_dump($app->config('languages')); 8 | //die; 9 | // setup default lang based on first in the list 10 | $lang = $availableLangs[0]; 11 | 12 | // if they are accessing the root, you could try and direct them to the correct language 13 | if ($env['PATH_INFO'] == '/') { 14 | if (isset($env['ACCEPT_LANGUAGE'])) { 15 | 16 | // try and auto-detect, find the language with the lowest offset as they are in order of priority 17 | $priority_offset = strlen($env['ACCEPT_LANGUAGE']); 18 | 19 | foreach ($availableLangs as $availableLang) { 20 | $i = strpos($env['ACCEPT_LANGUAGE'], $availableLang); 21 | if ($i !== false && $i < $priority_offset) { 22 | $priority_offset = $i; 23 | $lang = $availableLang; 24 | } 25 | } 26 | } 27 | } else { 28 | 29 | $pathInfo = $env['PATH_INFO'] . (substr($env['PATH_INFO'], -1) !== '/' ? '/' : ''); 30 | 31 | // extract lang from PATH_INFO 32 | foreach ($availableLangs as $availableLang) { 33 | $match = '/'.$availableLang; 34 | if (strpos($pathInfo, $match.'/') === 0) { 35 | $lang = $availableLang; 36 | $env['PATH_INFO'] = substr($env['PATH_INFO'], strlen($match)); 37 | 38 | if (strlen($env['PATH_INFO']) == 0) { 39 | $env['PATH_INFO'] = '/'; 40 | } 41 | } 42 | } 43 | } 44 | 45 | $app->view()->setLang($lang); 46 | $app->view()->setAvailableLangs($availableLangs); 47 | $app->view()->setPathInfo($env['PATH_INFO']); 48 | }); 49 | 50 | */ 51 | -------------------------------------------------------------------------------- /src/Rapyd/Views/Form.layout.twig: -------------------------------------------------------------------------------- 1 | {% extends 'form_div_layout.html.twig' %} 2 | 3 | 4 | {% block submit_widget %} 5 | {% spaceless %} 6 |
26 | Rapyd is a PHP microframework for PHP 5.3 built on top of Slim Framework, Twig, Symfony Forms, Illuminate/Database, Bootstrap 3.
27 | It implement MVC and CRUDL patterns.
28 |
29 | There is no much documentation about Rapyd, so this demo mudule is the way to find how to make things.
30 | Please do not hit me, Rapyd is built on top of well-documented projects.
31 |
46 | Slim Framework
47 | slim is basically a magnificent router, It's used as base of Rapyd/Application
48 | (a layer to implement Controllers and manage modules organization, routing etc.)
49 |
53 | Twig
54 | simply the best template engine (our Views are twig powered)
55 |
58 | Symfony Forms
59 | you can use Symfony Forms in rapyd, without using "Symfony"
60 |
63 | Illuminate/Database
64 | from laravel 4, It include DB Schema Builder, Fluent Query Builder, and the smart Eloquent ORM (our Models)
65 |
69 | Bootstrap
70 | one of the best HTML/CSS frameworks
71 |
92 | {{ code|raw }}
93 |
94 | #}
95 | {% endblock code %}
96 |
97 |
98 |
137 | $paramValue = $this->param('prefix.name'); // prefix[name] -> "string value"
138 | $paramValue = $this->param('prefix.name', 'post'); // prefix[name] -> "string value"
139 | $paramValue = $this->param('prefix.name', 'get'); // prefix[name] -> "string value"
140 | *
141 | *
142 | * @param mixed $name Name of the parameter
143 | * @param mixed $reqMode Optional mode. Either null (all params), true | "post"
144 | * (only POST params), false | "get" (only GET params)
145 | *
146 | * @return mixed Either array or single string or null
147 | */
148 | protected function param($name, $reqMode = null)
149 | {
150 | $args = array();
151 | if (is_array($name)) {
152 |
153 | // ["name"]
154 | if (count($name) === 1) {
155 | $name = $name[0];
156 | }
157 |
158 | // ["name", ["constraint" => "..", ..]]
159 | elseif (is_array($name[1])) {
160 | list($name, $args) = $name;
161 | }
162 |
163 | // ["name", "constraint" => "..", ..]
164 | else {
165 | $n = array_shift($name);
166 | $args = $name;
167 | $name = $n;
168 | }
169 | }
170 | $args = array_merge(array(
171 | 'constraint' => null,
172 | 'default' => null,
173 | 'raw' => false
174 | ), $args);
175 |
176 | // prefix name
177 | $name = $this->paramPrefix. $name;
178 |
179 | // determine method for request
180 | $reqMeth = $this->paramAccessorMeth($reqMode);
181 |
182 | // determine stash name
183 | $reqStashName = 'params'. ucfirst($reqMeth);
184 | if (is_null($this->$reqStashName)) {
185 | $this->$reqStashName = $this->request()->$reqMeth();
186 | }
187 | $params = $this->$reqStashName;
188 |
189 | // split of parts and go through
190 | $parts = preg_split('/\./', $name);
191 | while (isset($params[$parts[0]])) {
192 | $params = $params[$parts[0]];
193 | array_shift($parts);
194 | if (empty($parts)) {
195 | return $this->cleanupParam($params, $args);
196 | }
197 | }
198 |
199 | return null;
200 | }
201 |
202 | private function cleanupParam($value, $args)
203 | {
204 | if (is_array($value)) {
205 | foreach ($value as $k => $v) {
206 | $clean = $this->cleanupParam($v, $args);
207 | if (!is_null($clean)) {
208 | $value[$k] = $clean;
209 | }
210 | }
211 |
212 | return $value;
213 | } else {
214 |
215 | // cleanup
216 | if ($this->paramCleanup && !$args['raw']) {
217 | $value = preg_replace('/>/', '',
218 | preg_replace('/', '', $value ));
219 | }
220 |
221 | // check costraint
222 | if ($constraint = $args['constraint']) {
223 |
224 | // constraint = function & not matching
225 | if (is_object($constraint) && get_class($constraint) === 'Closure' && !$constraint($value)) {
226 | return null;
227 | }
228 |
229 | // constraint = regex & not matching
230 | elseif (!preg_match($constraint, $value)) {
231 | return null;
232 | }
233 | }
234 |
235 | return $value;
236 | }
237 | }
238 |
239 |
240 |
241 | /**
242 | * Reads multiple params at once
243 | *
244 | *
245 | $params = $this->params(['prefix.name', 'other.name']); // -> ["prefix.name" => "value", ..]
246 | $params = $this->params(['prefix.name', 'other.name'], true); // -> null if not all found
247 | $params = $this->params(['prefix.name', 'other.name'], ['other.name' => "Default Value"]);
248 | *
249 | *
250 | * @param mixed $name Name or names of parameters (GET or POST)
251 | * @param mixed $reqMode Optional mode. Either null (all params), true | "post"
252 | * (only POST params), false | "get" (only GET params)
253 | * @param mixed $defaults Either true (require ALL given or return null), array (defaults)
254 | *
255 | * @return mixed Either array or single string or null
256 | */
257 | protected function params($names = array(), $reqMode = null, $defaults = null)
258 | {
259 | // no names given -> get them all
260 | if (!$names) {
261 | $reqMeth = $this->paramAccessorMeth($reqMode);
262 | $params = $this->request()->$reqMeth();
263 | $namesPre = self::flatten($params);
264 | $names = array_keys($namesPre);
265 | if ($prefix = $this->paramPrefix) {
266 | $prefixLen = strlen($prefix);
267 | $names = array_map(function ($key) use ($prefixLen) {
268 | return substr($key, $prefixLen);
269 | }, array_filter($names, function ($in) use ($prefix) {
270 | return strpos($in, $prefix) === 0;
271 | }));
272 | }
273 | }
274 | $res = array();
275 | foreach ($names as $n) {
276 | $param = $this->param($n, $reqMode);
277 | if (!is_null($param) && (!is_array($param) || !empty($param))) {
278 | $res[$n] = $param;
279 | }
280 |
281 | // if in "need all" mode
282 | elseif ($defaults === true) {
283 | return null;
284 | }
285 |
286 | // if in default mode
287 | elseif (is_array($defaults) && isset($defaults[$n])) {
288 | $res[$n] = $defaults[$n];
289 | }
290 | }
291 |
292 | return $res;
293 | }
294 |
295 |
296 | protected function paramAccessorMeth($reqMode = null)
297 | {
298 | return $reqMode === true || $reqMode === 'post' // POST
299 | ? 'post'
300 | : ($reqMode === false || $reqMode === 'get' // GET
301 | ? 'get'
302 | : 'params' // ALL
303 | );
304 | }
305 |
306 |
307 |
308 | protected static function flatten($data, $flat = array(), $prefix = '')
309 | {
310 | foreach ($data as $key => $value) {
311 |
312 | // is array -> flatten deeped
313 | if (is_array($value)) {
314 | $flat = self::flatten($value, $flat, $prefix. $key. '.');
315 | }
316 | // scalar -> use
317 | else {
318 | $flat[$prefix. $key] = $value;
319 | }
320 | }
321 |
322 | return $flat;
323 | }
324 |
325 | }
326 |
--------------------------------------------------------------------------------
/src/Rapyd/Helpers/Url.php:
--------------------------------------------------------------------------------
1 | 1,
20 | 'reset'=>1,
21 | 'pag'=>1,
22 | 'orderby'=>2,
23 | 'show'=>1,
24 | 'create'=>1,
25 | 'modify'=>1,
26 | 'delete'=>1,
27 | 'insert'=>1,
28 | 'update'=>1,
29 | 'do_delete'=>1,
30 | 'process'=>1);
31 |
32 | public function __construct($app)
33 | {
34 | $this->app = $app;
35 |
36 | return $this;
37 | }
38 |
39 | public static function unparse_str($array)
40 | {
41 | return '?' . preg_replace('/%5B[0-9]+%5D/simU', '[]', http_build_query($array));
42 | }
43 |
44 | public function set($url)
45 | {
46 | $this->url = $url;
47 |
48 | return $this;
49 | }
50 |
51 | public function get()
52 | {
53 | if ($this->url == '') {
54 | return $this->current();
55 | } else {
56 | $url = $this->url;
57 | $this->url = '';
58 |
59 | return $url;
60 | }
61 | }
62 |
63 | public function current()
64 | {
65 | //var_dump($this->app->router()->getCurrentRoute()->getPattern());
66 | //die;
67 |
68 | if (isset($_SERVER['HTTP_X_ORIGINAL_URL']))
69 | $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
70 | $url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : @getenv('REQUEST_URI');
71 |
72 | return $url;
73 | }
74 |
75 | public function append($key, $value)
76 | {
77 | return ($this->from == 'uri') ? $this->appendUri($key, $value) : $this->appendQS($key, $value);
78 | }
79 |
80 | public function remove($key, $params = 1)
81 | {
82 | return ($this->from == 'uri') ? $this->removeUri($key, $params) : $this->removeQS($key);
83 | }
84 |
85 | public function removeAll($key)
86 | {
87 | return ($this->from == 'uri') ? $this->removeAllUri($key) : $this->removeAllQS($key);
88 | }
89 |
90 | public function replace($key, $newkey)
91 | {
92 | return ($this->from == 'uri') ? $this->replaceUri($key, $newkey) : $this->replaceQS($key, $newkey);
93 | }
94 |
95 | public function value($key, $default = false)
96 | {
97 | return ($this->from == 'uri') ? $this->valueUri($key, $default) : $this->valueQS($key, $default);
98 | }
99 |
100 | public function appendQS($key, $value)
101 | {
102 | $url = $this->get();
103 | $qs_array = array();
104 | if (strpos($url, '?') !== false) {
105 | $qs = substr($url, strpos($url, '?') + 1);
106 | $url = substr($url, 0, strpos($url, '?'));
107 | parse_str($qs, $qs_array);
108 | }
109 | $qs_array[$key] = $value;
110 | $query_string = self::unparse_str($qs_array);
111 | $this->url = $url . $query_string;
112 |
113 | return $this;
114 | }
115 |
116 | public function removeQS($keys)
117 | {
118 | $qs_array = array();
119 | $url = $this->get();
120 | if (strpos($url, '?') === false) {
121 | $this->url = $url;
122 |
123 | return $this;
124 | }
125 | $qs = substr($url, strpos($url, '?') + 1);
126 | $url = substr($url, 0, strpos($url, '?'));
127 | parse_str($qs, $qs_array);
128 |
129 | if (!is_array($keys)) {
130 | if ($keys == 'ALL') {
131 | $this->url = $url;
132 |
133 | return $this;
134 | }
135 | $keys = array($keys);
136 | }
137 | foreach ($keys as $key) {
138 | unset($qs_array[$key]);
139 | }
140 | $query_string = self::unparse_str($qs_array);
141 |
142 | $this->url = $url . $query_string;
143 |
144 | return $this;
145 | }
146 |
147 | public function removeAllQS($cid = null)
148 | {
149 | $semantic = array_keys($this->semantic);
150 | if (isset($cid)) {
151 |
152 | foreach ($semantic as $key) {
153 | $keys[] = $key . $cid;
154 | }
155 | $semantic = $keys;
156 | }
157 |
158 | return $this->remove($semantic);
159 | }
160 |
161 | public function replaceQS($key, $newkey)
162 | {
163 | $qs_array = array();
164 | $url = $this->get();
165 | if (strpos($url, '?') !== false) {
166 | $qs = substr($url, strpos($url, '?') + 1);
167 | $url = substr($url, 0, strpos($url, '?'));
168 | parse_str($qs, $qs_array);
169 | }
170 | if (isset($qs_array[$key])) {
171 | $qs_array[$newkey] = $qs_array[$key];
172 | unset($qs_array[$key]);
173 | }
174 | $query_string = self::unparse_str($qs_array);
175 | $this->url = $url . $query_string;
176 |
177 | return $this;
178 | }
179 |
180 | public function valueQS($key, $default = false)
181 | {
182 | if (strpos($key, '|')) {
183 | $keys = explode('|', $key);
184 | foreach ($keys as $k) {
185 | $v = $this->valueQS($k, $default);
186 | if ($v != $default)
187 | return $v;
188 | }
189 |
190 | return $default;
191 | }
192 |
193 | parse_str(parse_url($this->current(), PHP_URL_QUERY), $params);
194 | if (strpos($key, '.')) {
195 | list($namespace, $subkey) = explode('.', $key);
196 |
197 | return (isset($params[$namespace][$key])) ? $params[$namespace][$key] : $default;
198 | } else {
199 | return (isset($params[$key])) ? $params[$key] : $default;
200 | }
201 | }
202 |
203 | //-------------------------------
204 |
205 | public function appendUri($key, $value = null)
206 | {
207 |
208 | $url = $this->get();
209 | $seg_array = explode("/", trim($url, '/'));
210 |
211 | $key_position = array_search($key, $seg_array);
212 | if ($key_position !== false) {
213 |
214 | array_splice($seg_array, $key_position, count((array) $value) + 1, array_merge((array) $key, array_map('strval', (array) $value)));
215 | } else {
216 | $seg_array = array_merge($seg_array, array_merge((array) $key, array_map('strval', (array) $value)));
217 | }
218 |
219 | $this->url = "/".implode('/', $seg_array);
220 |
221 | return $this;
222 |
223 | }
224 |
225 | public function removeUri($keys, $params = 1)
226 | {
227 | $url = $this->get();
228 | $seg_array = explode("/", trim($url, '/'));
229 |
230 | if (!is_array($keys)) {
231 | $keys = array($keys);
232 | }
233 |
234 | foreach ($keys as $key) {
235 | $key_position = array_search($key, $seg_array);
236 | if ($key_position !== false) {
237 |
238 | $kkey = preg_replace('@\d*$@', '', $key);
239 | if (isset($this->semantic[$kkey])) {
240 | array_splice($seg_array, $key_position, $this->semantic[$kkey] + 1);
241 | } else {
242 | array_splice($seg_array, $key_position, $params + 1);
243 | }
244 | }
245 | }
246 |
247 | $this->url = "/".implode('/', $seg_array);
248 |
249 | return $this;
250 | }
251 |
252 | public function removeAllUri($cid = null)
253 | {
254 | $url = $this->get();
255 | $keys = array();
256 | if (isset($cid)) {
257 | foreach ($this->semantic as $key => $params) {
258 | $keys[] = $key . $cid;
259 | }
260 | $this->url = $this->removeUri($keys)->get();
261 |
262 | } else {
263 |
264 | $uri = $this->get();
265 | $seg_array = explode("/", trim($uri, '/'));
266 | foreach ($this->semantic as $key => $params) {
267 | if (preg_match_all('@(' . $key . '\d*)@', $url, $matches))
268 | foreach ($matches[1] as $mkey) {
269 | $this->url = $this->removeUri($mkey, $this->semantic[$key] + 1)->get();
270 | }
271 | }
272 | }
273 |
274 | return $this;
275 | }
276 |
277 | public function replaceUri($key, $newkey, $url = null)
278 | {
279 | $url = $this->get();
280 | $seg_array = explode("/", trim($url, '/'));
281 |
282 | $key_position = array_search($key, $seg_array);
283 | if ($key_position !== false) {
284 | array_splice($seg_array, $key_position, 1, array($newkey));
285 | }
286 | $this->url = "/".implode('/', $seg_array);
287 |
288 | return $this;
289 |
290 | }
291 |
292 | public function valueUri($key, $default = false, $params = 1)
293 | {
294 |
295 | $url = $this->get();
296 | $seg_array = explode("/", trim($url, '/'));
297 |
298 | if (strpos($key, '|')) {
299 | $keys = explode('|', $key);
300 | foreach ($keys as $k) {
301 | $v = $this->valueUri($k, $default);
302 | if ($v != $default)
303 | return $v;
304 | }
305 |
306 | return $default;
307 | }
308 |
309 | if (strpos($key, '.')) {
310 | //...
311 | } else {
312 | $key_position = array_search($key, $seg_array);
313 | if ($key_position !== false) {
314 | if (isset($seg_array[$key_position + 1])) {
315 | //..
316 | $skey = preg_replace("@([a-z]+)\d@", "\\1", $key);
317 |
318 | if (isset($this->semantic[$skey])) {
319 | if ($this->semantic[$skey] == 0)
320 | return true;
321 | elseif ($this->semantic[$skey] < 2)
322 | return $seg_array[$key_position + 1];
323 | else
324 | return array_slice($seg_array, $key_position + 1, $this->semantic[$skey]);
325 | } else {
326 | if ($params == 0)
327 | return true;
328 | elseif ($params < 2)
329 | return $seg_array[$key_position + 1];
330 | else
331 | return array_slice($seg_array, $key_position + 1, $params);
332 | }
333 | } else {
334 | return true;
335 | }
336 | }
337 | }
338 |
339 | return $default;
340 | }
341 |
342 | }
343 |
--------------------------------------------------------------------------------
/src/Rapyd/Application.php:
--------------------------------------------------------------------------------
1 | setupDatabase();
84 | $this->setupView();
85 | $this->setupForms();
86 | $this->setupWidgets();
87 | //custom call, nothing to setup
88 | } else {
89 |
90 | parent::__construct($config);
91 | }
92 |
93 | $this->url = new \Rapyd\Helpers\Url($this);
94 | $this->url->from = $config['url_method'];
95 | }
96 |
97 | public function setupRoute($routes = array())
98 | {
99 | //widgets route conditions
100 | include __DIR__ . '/Config/routes.php';
101 |
102 | require __DIR__.'/../src/App/Config/hooks.php';
103 | require __DIR__.'/../src/Rapyd/Config/routes.php';
104 | require __DIR__.'/../src/App/Config/routes.php';
105 | require __DIR__.'/../src/Modules/Demos/Config/routes.php';
106 |
107 | //add default routes
108 | if (empty($routes)) {
109 | $routes = include __DIR__ . '/../App/Config/routes.php';
110 |
111 | $module_dir = __DIR__ . '/../Modules/';
112 | if (file_exists($module_dir)) {
113 | $modules = array_diff(scandir($module_dir), array('..', '.'));
114 | foreach ($modules as $module) {
115 | if (file_exists($module_dir . $module . '/Config/routes.php')) {
116 | $module_routes = include $module_dir . $module . '/Config/routes.php';
117 | $this->addRoutes($module_routes);
118 | }
119 | }
120 | }
121 | }
122 | $this->addRoutes($routes);
123 | }
124 |
125 | public function setupDatabase($db = array())
126 | {
127 | //add default routes
128 | if (empty($db))
129 | $db = include __DIR__ . '/../App/Config/db.php';
130 |
131 | $capsule = new Capsule();
132 | $capsule->addConnection($db, 'default');
133 | $capsule->container['config']['database.fetch'] = \PDO::FETCH_CLASS;
134 | $capsule->bootEloquent();
135 |
136 | // setup db
137 | $this->db = $capsule->getConnection('default');
138 | }
139 |
140 | public function setupView(array $twig = array())
141 | {
142 | //add default routes
143 | if (empty($twig))
144 | $twig = include __DIR__ . '/../App/Config/twig.php';
145 |
146 | // Prepare view to use twig
147 | $this->view(new \Slim\Views\Twig());
148 | $this->view->parserOptions = $twig;
149 |
150 | $views_arr = array(VIEWS_DIR, VENDOR_TWIG_BRIDGE_DIR . '/Resources/views/Form');
151 |
152 | $module_dir = dirname(__DIR__) . '/Modules/';
153 | if (file_exists($module_dir)) {
154 | $modules = array_diff(scandir($module_dir), array('..', '.'));
155 | foreach ($modules as $module) {
156 | if (file_exists($module_dir . $module . '/Views')) {
157 | $views_arr[] = $module_dir . $module . '/Views';
158 | }
159 | }
160 | }
161 | $views_arr[] = __DIR__ . '/Views';
162 | $this->view->twigTemplateDirs = $views_arr;
163 |
164 | $this->view->parserExtensions = array(
165 | new Twig_Extension_Debug()
166 | );
167 |
168 | //to move somewhere
169 | $function = new Twig_SimpleFunction('active_class', function ($path, $class = " class=\"active\"") {
170 | return (preg_match("#{$path}#", $_SERVER["REQUEST_URI"])) ? $class : '';
171 | }, array('is_safe' => array('html')));
172 | $this->view->getInstance()->addFunction($function);
173 |
174 | $function = new Twig_SimpleFunction('source_code', function ($filepath) {
175 | $code = file_get_contents(VIEWS_DIR . $filepath);
176 | $code = preg_replace("#{% block code %}.*{% endblock %}#Us", '', $code);
177 | $code = highlight_string($code, true);
178 |
179 | return "\n" . $code . "\n"; 180 | }, array('is_safe' => array('html'))); 181 | $this->view->getInstance()->addFunction($function); 182 | } 183 | 184 | protected function setupForms() 185 | { 186 | 187 | // Set up the CSRF provider 188 | $csrfProvider = new DefaultCsrfProvider(CSRF_SECRET); 189 | 190 | // Set up the Translation component 191 | $translator = new Translator('en'); 192 | $translator->setFallbackLocale(array('en')); 193 | 194 | $translator->setLocale('en'); 195 | $translator->addLoader('xlf', new XliffFileLoader()); 196 | $translator->addResource('xlf', VENDOR_FORM_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); 197 | $translator->addResource('xlf', VENDOR_VALIDATOR_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); 198 | 199 | // Set up the Validator component 200 | $validator = Validation::createValidatorBuilder() 201 | ->setTranslator($translator) 202 | ->setTranslationDomain('validators') 203 | ->getValidator(); 204 | 205 | $formEngine = new TwigRendererEngine(array(DEFAULT_FORM_THEME)); 206 | 207 | $twig = $this->view->getInstance(); 208 | $formEngine->setEnvironment($twig); 209 | $twig->addExtension(new TranslationExtension($translator)); 210 | $twig->addExtension(new FormExtension(new TwigRenderer($formEngine, $csrfProvider))); 211 | 212 | // Set up the Form component 213 | $this->form = Forms::createFormFactoryBuilder() 214 | ->addExtension(new CsrfExtension($csrfProvider)) 215 | ->addExtension(new ValidatorExtension($validator)) 216 | ->getFormFactory(); 217 | } 218 | 219 | protected function setupWidgets() 220 | { 221 | $this->grid = new \Rapyd\Widgets\WidgetBuilder("\\Rapyd\\Widgets\\DataGrid"); //new \Rapyd\Helpers\Url;//new WidgetBuilder('DataGrid'); 222 | $this->set = new \Rapyd\Widgets\WidgetBuilder("\\Rapyd\\Widgets\\DataSet"); 223 | 224 | } 225 | 226 | public function addRoutes(array $routings, $condition = null) 227 | { 228 | foreach ($routings as $path => $args) { 229 | $httpMethod = 'any'; 230 | 231 | // simple 232 | if (!is_array($args)) { 233 | $args = array($args); 234 | } 235 | 236 | // specific HTTP method 237 | if (count($args) > 1 && is_string($args[1])) { 238 | $classAction = array_shift($args); 239 | $httpMethod = array_shift($args); 240 | array_unshift($args, $classAction); 241 | } 242 | 243 | // readd path & extract route 244 | array_unshift($args, $path); 245 | $this->extractControllerFromRoute($args, $condition); 246 | 247 | // call "map" method to add routing 248 | $route = call_user_func_array(array($this, 'map'), $args); 249 | if ('any' === $httpMethod) { 250 | $route->via('GET', 'POST'); 251 | } else { 252 | $route->via(strtoupper($httpMethod)); 253 | } 254 | if ($condition) { 255 | $route->conditions($condition); 256 | } 257 | } 258 | 259 | return $this; 260 | } 261 | 262 | protected function extractControllerFromRoute(array &$args, $condition = null) 263 | { 264 | // tmp remove path 265 | $path = array_shift($args); 266 | 267 | // determine prefix (eg "\Vendor\Bundle\Controller") 268 | $classNamePrefix = isset($this->settings['controller.class_prefix']) ? $this->settings['controller.class_prefix'] . '\\' : ''; 269 | 270 | // determine method suffix or default to "Action" 271 | $methodNameSuffix = isset($this->settings['controller.method_suffix']) ? $this->settings['controller.method_suffix'] : 'Action'; 272 | $methodName = ''; 273 | $className = array_shift($args); 274 | if (strpos($className, '\\') !== 0) { 275 | $className = $classNamePrefix . $className; 276 | } 277 | 278 | // having
167 | * $edit->pre_process(array('update'), array($this, 'some_method'));
168 | * ..
169 | * function some_method($model)
170 | * {
171 | * //do checks.. etc..
172 | * $model->set('afield', 'avalue');
173 | * }
174 | *
175 | *
176 | * @todo replace/rename it using ->callback() or something similar
177 | * @param type $action
178 | * @param type $function
179 | * @param type $arr_values
180 | */
181 | public function pre_process($action, $function, $arr_values = array())
182 | {
183 | $this->model->pre_process($action, $function, $arr_values);
184 | }
185 |
186 | /**
187 | * usage:
188 | *
189 | * $edit->post_process(array('insert'), array($this, 'some_method'));
190 | * ..
191 | * function some_method($model)
192 | * {
193 | * //do checks.. etc..
194 | * $model->set('afield', 'avalue');
195 | * }
196 | *
197 | *
198 | * @param type $action
199 | * @param type $function
200 | * @param type $arr_values
201 | */
202 | public function post_process($action, $function, $arr_values = array())
203 | {
204 | $this->model->post_process($action, $function, $arr_values);
205 | }
206 |
207 | /**
208 | * internal, build each field then form
209 | *
210 | * @return string compiled form
211 | */
212 | protected function build_form()
213 | {
214 | html_helper::css('widgets/dataform/assets/dataform.css');
215 | $data = get_object_vars($this);
216 | $data['container'] = $this->button_containers();
217 | $form_type = 'open';
218 | // See if we need a multipart form
219 | foreach ($this->fields as $field_obj) {
220 | if ($field_obj instanceof upload_field) {
221 | $form_type = 'open_multipart';
222 | break;
223 | }
224 | }
225 | // Set the form open and close
226 | if ($this->status_is('show')) {
227 | $data['form_begin'] = '' . implode('
', (!is_array($message)) ? array($message) : $message) . '
'; 397 | } 398 | 399 | /** 400 | * nest a content inside a form, it can be called onlt after a build() bacause it work on compiled output 401 | * 402 | * @param string $field_id id of container 403 | * @param string $content content to be nested 404 | */ 405 | public function nest($field_id, $content) 406 | { 407 | if ($this->output != "") { 408 | $nesting_point = 'id="' . $field_id . '">'; 409 | $this->output = str_replace($nesting_point, $nesting_point . $content, $this->output); 410 | } 411 | } 412 | 413 | } 414 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/Widget.php: -------------------------------------------------------------------------------- 1 | array(), "BL" => array(), "BR" => array()); 29 | public $buttons = array(); 30 | public $url; 31 | 32 | public function __construct($config = array()) 33 | { 34 | if (count($config) > 0) { 35 | $this->initialize($config); 36 | } 37 | $this->parser = new \Rapyd\Helpers\Parser; 38 | } 39 | 40 | /** 41 | * called by constructor, a widget can be built using an associative array to prefill each widget property 42 | * it works calling $widget->set_$key($val) for each item in config array. 43 | * since each widget is initialized this way, you can for example build a complex "dataedit" 44 | * using a sigle multi-dimensional config array, defining form properties and fields properties 45 | * (so this configuration can be stored or manipulated in an easy way) 46 | * 47 | *
48 | * //edit
49 | * $config = array (
50 | * 'label' => 'Manage Article',
51 | * 'source' => 'articles',
52 | * 'back_url' => $this->url('filtered_grid'),
53 | * 'fields' => array (
54 | * array (
55 | * 'type' => 'input',
56 | * 'name' => 'title',
57 | * 'label' => 'Title',
58 | * 'rule' => 'trim|required',
59 | * ),
60 | *
61 | * ),
62 | * 'buttons' => array('modify','save','undo','back'),
63 | * );
64 | * $edit = new \Rapyd\Widget\DataEdit($config);
65 | * $edit->build();
66 | *
67 | *
68 | * @param array $config
69 | */
70 | public function initialize($config = array())
71 | {
72 | $this->clear();
73 | foreach ($config as $key => $val) {
74 | if (method_exists($this,'set' . ucfirst($key))) {
75 | call_user_func_array(array($this, 'set' . ucfirst($key)), (array) $val);
76 | }
77 | }
78 | }
79 |
80 | /**
81 | * reset each property to default values
82 | */
83 | public function clear()
84 | {
85 | $vars = get_class_vars(get_class($this));
86 |
87 | foreach ($vars as $property => $value) {
88 | if (!in_array($property, array("cid", "built"))) {
89 | if (!isset($this::$$property))
90 | $this->$property = $value;
91 | }
92 | }
93 | }
94 |
95 | /**
96 | * identifier is empty or a numeric value, it "identify" a single object instance.
97 | * by default if you build 3 widget in a single controller/page their id will be:
98 | * "" for ther first one
99 | * 1 for the second one
100 | * 2 for the third
101 | * .. etcetera
102 | *
103 | * identifiers are used to preserve uri/url segments consistence/isolation
104 | * so for example you can build 2 datagrid on a single controller without problem because uri-semantic will be:
105 | * /controller/method/[[orderby/article_id/desc/pag/1]]/[[orderby1/anotherfield/desc/pag1/2]]
106 | *
107 | * @return string identifier
108 | */
109 | protected function getIdentifier()
110 | {
111 | if (self::$identifier < 1) {
112 | self::$identifier++;
113 |
114 | return "";
115 | }
116 |
117 | return (string) self::$identifier++;
118 | }
119 |
120 | /**
121 | * dynamic getter & setter
122 | *
123 | * it's used basically to ensure method chaining and to get & set widget's properties
124 | * it also enable "short array" syntax so you can use $widget->method('param|param') and it will call
125 | * $widget->set_method('param','param')
126 | *
127 | * @param string $method
128 | * @param array $arguments
129 | * @return object $this
130 | */
131 | public function __call($method, $arguments)
132 | {
133 | $prefix = strtolower(substr($method, 0, 3));
134 | $property = strtolower(substr($method, 3));
135 | if (method_exists($this, 'set' . ucfirst($method))) {
136 | return call_user_func_array(array($this, 'set' . ucfirst($method)), $arguments);
137 | }
138 |
139 | if (empty($prefix) || empty($property)) {
140 | return;
141 | }
142 |
143 | if ($prefix == "get" && isset($this->$property)) {
144 | return $this->$property;
145 | }
146 |
147 | if ($prefix == "set") {
148 | if
149 | (
150 | !in_array($property, array('cell_template', 'pattern'))
151 | and is_string($arguments[0])
152 | and strpos($arguments[0], '|')
153 | )
154 | {
155 | //die ($property);
156 | $this->$property = explode('|', $arguments[0]);
157 | } else {
158 | $this->$property = $arguments[0];
159 | }
160 |
161 | return $this;
162 | }
163 | }
164 |
165 | /**
166 | * shortcut for $widget->status == $status OR ....
167 | * so you can use $widget->status_is($status)
168 | * where $status can be an array of valid status
169 | * not very useful
170 | *
171 | * @todo merge all "property_is/on" in one call
172 | * @param string $status
173 | * @return bool
174 | */
175 | public function status_is($status = "false")
176 | {
177 | if (is_array($status))
178 | return (bool) in_array($this->status, $status);
179 | return ($this->status == $status);
180 | }
181 |
182 | /**
183 | * shortcut for $widget->process_status == $process_status OR ....
184 | * so you can use $widget->on($process_status)
185 | * where $process_status can be an array of valid status
186 | *
187 | * @todo merge all "property_is/on" in one call
188 | * @param string $process_status
189 | * @return bool
190 | */
191 | public function on($process_status = "false")
192 | {
193 | if (is_array($process_status))
194 | return (bool) in_array($this->process_status, $process_status);
195 | return ($this->process_status == $process_status);
196 | }
197 |
198 | /**
199 | * shortcut for $widget->action == $action OR ....
200 | * so you can use $widget->action_is($action)
201 | * where $process_status can be an array of valid actions
202 | * not very useful
203 | *
204 | * @todo merge all "property_is/on" in one call
205 | * @param string $process_status
206 | * @return bool
207 | */
208 | public function action_is($action = "false")
209 | {
210 | if (is_array($action))
211 | return (bool) in_array($this->action, $action);
212 | return ($this->action == $action);
213 | }
214 |
215 | /**
216 | * alias for action_is
217 | *
218 | * @param string $action
219 | * @return bool
220 | */
221 | public function when($action = "false")
222 | {
223 | return $this->action_is($action);
224 | }
225 |
226 | /**
227 | * important stuff, widgets support placeholders like: {placeholder}
228 | * parse_pattern find all occurences and return a simple array of matches
229 | * it's used for example to find "field" placeholders inside a datagrid column pattern
230 | *
231 | * @param string $pattern
232 | * @return array of matches {placeholders}
233 | */
234 | public static function parse_pattern($pattern)
235 | {
236 | if (preg_match_all('/\{(\w+)\}/is', $pattern, $matches)) {
237 | return $matches[1];
238 | }
239 | }
240 |
241 | /**
242 | * replace placeholders in a pattern like "bla bla.. {placeholder}"
243 | *
244 | * @param string $pattern a string containing {placeholder}s
245 | * @param array $values associative array like array('placeholder'=>'value', 'placeholder2'=>'value2')
246 | * @return string parsed output
247 | */
248 | public static function replace_pattern($pattern, $values)
249 | {
250 | $output = $pattern;
251 | $output = str_replace('|', '£|£', $output); //fix for params separator
252 | $matches = self::parse_pattern($pattern);
253 | if ($matches) {
254 | foreach ($matches as $field) {
255 | if (isset($values[$field]) or is_null($values[$field]))
256 | $output = str_replace('{' . $field . '}', $values[$field], $output);
257 | }
258 | }
259 |
260 | return $output;
261 | }
262 |
263 |
264 | /**
265 | * basically it "flat" buttons array for each zone
266 | * used on build() to prepare button output
267 | * zones are "TL","TR","BL","BR" (top-left, top-right, bottom-left, bottom-right)
268 | * see button()
269 | *
270 | * @todo i think it must be improved..
271 | * @param string $container
272 | * @return array
273 | */
274 | public function button_containers($container = null)
275 | {
276 | if (isset($container)) {
277 | return join(" ", $this->button_container[$container]);
278 | } else {
279 | foreach ($this->button_container as $container => $content) {
280 | $containers[$container] = join(" ", $this->button_container[$container]);
281 | }
282 |
283 | return $containers;
284 | }
285 | }
286 |
287 | /**
288 | * add a button to the button container in a given position
289 | * positions are "TL","TR","BL","BR" (top-left, top-right, bottom-left, bottom-right)
290 | *
291 | * @param string $name button name attribute
292 | * @param string $caption button label
293 | * @param string $action the onclick event (all buttons currently works using window.location.href=...)
294 | * @param string $position "TL","TR","BL","BR"
295 | * @param string $class css class
296 | */
297 | public function button($name, $caption, $action, $position="BL", $class="btn")
298 | {
299 | $this->button_container[$position][] = html_helper::button($name, $caption, $action, "button", $class);
300 | }
301 |
302 | /**
303 | * same of button() but it make a submit
304 | *
305 | * @param string $name
306 | * @param string $caption
307 | * @param string $position
308 | */
309 | public function submit($name, $caption, $position="BL")
310 | {
311 | $this->button_container[$position][] = html_helper::button($name, $caption, "", "submit", "btn");
312 | }
313 |
314 | /**
315 | *
316 | * @param type $config
317 | */
318 | public function action_button($config)
319 | {
320 |
321 | $caption = (isset($config['caption'])) ? $config['caption'] : "Azione";
322 | if (isset($config['popup']) || isset($config['target'])) {
323 | $config['popup'] = (isset($config['popup'])) ? ",'mywin','" . $config['popup'] . "'" : "";
324 | $action = "javascript:window.open('" . $config['url'] . "'" . $config['popup'] . ")";
325 | } else {
326 | if (isset($config['confirm'])) {
327 | $action = "javascript:if (confirm('" . addslashes($config['confirm']) . "')) { window.location='" . $config['url'] . "' }";
328 | } else {
329 | $action = "javascript:window.location='" . $config['url'] . "'";
330 | }
331 | }
332 | $position = (isset($config['position'])) ? $config['position'] : "TR";
333 | $class = (isset($config['class'])) ? $config['class'] : "btn";
334 | $this->button("btn_act", $caption, $action, $position, $class);
335 | }
336 |
337 |
338 | /**
339 | * it exppect parameters like a string, an array, a serialized array 'save|delete|undo...'
340 | * or an associative array, and fill the "$widget->buttons" array
341 | * then buil_buttons will cycle this array to make buttons
342 | *
343 | */
344 | public function set_buttons()
345 | {
346 |
347 | $buttons = func_get_args();
348 |
349 | //catch buttons => 'save|delete|undo...';
350 | if (is_string($buttons[0]) and strpos($buttons[0], '|')) {
351 | $buttons = explode('|', $buttons[0]);
352 | }
353 |
354 | if (func_num_args() == 1 and is_array($buttons[0])) {
355 | $buttons = $buttons[0];
356 | }
357 |
358 | foreach ($buttons as $name => $button) {
359 | if (is_numeric($name) and is_string($button)) {
360 | if (strpos($button, '|')) {
361 | list($button['type'], $button['caption']) = explode('|', $button);
362 | } else {
363 | $button = array('type' => $button);
364 | }
365 | } else {
366 | if (is_string($button) and strpos($button, '|')) {
367 | $btn = array();
368 | @list($btn['name'], $btn['caption'], $btn['url'], $btn['position'], $btn['class']) = explode('|', $button);
369 | $button = $btn;
370 | }
371 | if (!isset($button['type']))
372 | $button['type'] = $name;
373 | }
374 |
375 | $this->buttons[] = $button;
376 | }
377 | }
378 |
379 |
380 | /**
381 | * this function cycle $widget->buttons array and call all "xxx_button()" methods
382 | *
383 | * @todo I should really consider moving it from here
384 | */
385 | public function build_buttons()
386 | {
387 | foreach ($this->buttons as $button) {
388 | $build_button = $button['type'] . "_button";
389 | if (count($button) < 2) {
390 | $this->$build_button();
391 | } else {
392 | $this->$build_button($button);
393 | }
394 | }
395 | $this->buttons = array();
396 | }
397 |
398 | /**
399 | * "echo $widget" automatically call build() it and display $widget->output
400 | * however explicit build is preferred for a clean code
401 | *
402 | * @return string
403 | */
404 | public function __toString()
405 | {
406 | if ($this->output == "")
407 | $this->build();
408 |
409 | return $this->output;
410 | }
411 |
412 | }
413 |
--------------------------------------------------------------------------------