OSDN Git Service

e9ef506936f9865fae6e289767c47686762405b3
[ethna/ethna.git] / class / Ethna_SmartyPlugin.php
1 <?php
2 /**
3  *  Ethna_SmartyPlugin.php
4  *
5  *  @author     Masaki Fujimoto <fujimoto@php.net>
6  *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
7  *  @package    Ethna
8  *  @version    $Id$
9  */
10
11 // {{{ smarty_modifier_number_format
12 /**
13  *  smarty modifier:number_format()
14  *
15  *  number_format()関数のwrapper
16  *
17  *  sample:
18  *  <code>
19  *  {"12345"|number_format}
20  *  </code>
21  *  <code>
22  *  12,345
23  *  </code>
24  *
25  *  @param  string  $string フォーマット対象文字列
26  *  @return string  フォーマット済み文字列
27  */
28 function smarty_modifier_number_format($string)
29 {
30     if ($string === "" || $string == null) {
31         return "";
32     }
33     return number_format($string);
34 }
35 // }}}
36
37 // {{{ smarty_modifier_strftime
38 /**
39  *  smarty modifier:strftime()
40  *
41  *  strftime()関数のwrapper
42  *
43  *  sample:
44  *  <code>
45  *  {"2004/01/01 01:01:01"|strftime:"%Y年%m月%d日"}
46  *  </code>
47  *  <code>
48  *  2004年01月01日
49  *  </code>
50  *
51  *  @param  string  $string フォーマット対象文字列
52  *  @param  string  $format 書式指定文字列(strftime()関数参照)
53  *  @return string  フォーマット済み文字列
54  */
55 function smarty_modifier_strftime($string, $format)
56 {
57     if ($string === "" || $string == null) {
58         return "";
59     }
60     return strftime($format, strtotime($string));
61 }
62 // }}}
63
64 // {{{ smarty_modifier_count
65 /**
66  *  smarty modifier:count()
67  *
68  *  count()関数のwrapper
69  *
70  *  sample:
71  *  <code>
72  *  $smarty->assign("array", array(1, 2, 3));
73  *
74  *  {$array|@count}
75  *  </code>
76  *  <code>
77  *  3
78  *  </code>
79  *
80  *  @param  array   $array  対象となる配列
81  *  @return int     配列の要素数
82  */
83 function smarty_modifier_count($array)
84 {
85     return count($array);
86 }
87 // }}}
88
89 // {{{ smarty_modifier_join
90 /**
91  *  smarty modifier:join()
92  *
93  *  join()関数のwrapper
94  *
95  *  sample:
96  *  <code>
97  *  $smarty->assign("array", array(1, 2, 3));
98  *
99  *  {$array|@join:":"}
100  *  </code>
101  *  <code>
102  *  1:2:3
103  *  </code>
104  *
105  *  @param  array   $array  join対象の配列
106  *  @param  string  $glue   連結文字列
107  *  @return string  連結後の文字列
108  */
109 function smarty_modifier_join($array, $glue)
110 {
111     if (is_array($array) == false) {
112         return $array;
113     }
114     return implode($glue, $array);
115 }
116 // }}}
117
118 // {{{ smarty_modifier_filter
119 /**
120  *  smarty modifier:filter()
121  *
122  *  指定された連想配列のうち$keyで指定された要素のみを配列に再構成する
123  *
124  *  sample:
125  *  <code>
126  *  $smarty->assign("array", array(
127  *      array("foo" => 1, "bar" => 4),
128  *      array("foo" => 2, "bar" => 5),
129  *      array("foo" => 3, "bar" => 6),
130  *  ));
131  *
132  *  {$array|@filter:"foo"|@join:","}
133  *  </code>
134  *  <code>
135  *  1,2,3
136  *  </code>
137  *  
138  *  @param  array   $array  filter対象となる配列
139  *  @param  string  $key    抜き出して配列を構成する連想配列のキー
140  *  @return array   再構成された配列
141  */
142 function smarty_modifier_filter($array, $key)
143 {
144     if (is_array($array) == false) {
145         return $array;
146     }
147     $tmp = array();
148     foreach ($array as $v) {
149         if (isset($v[$key]) == false) {
150             continue;
151         }
152         $tmp[] = $v[$key];
153     }
154     return $tmp;
155 }
156 // }}}
157
158 // {{{ smarty_modifier_unique
159 /**
160  *  smarty modifier:unique()
161  *
162  *  unique()関数のwrapper
163  *
164  *  sample:
165  *  <code>
166  *  $smarty->assign("array1", array("a", "a", "b", "a", "b", "c"));
167  *  $smarty->assign("array2", array(
168  *      array("foo" => 1, "bar" => 4),
169  *      array("foo" => 1, "bar" => 4),
170  *      array("foo" => 1, "bar" => 4),
171  *      array("foo" => 2, "bar" => 5),
172  *      array("foo" => 3, "bar" => 6),
173  *      array("foo" => 2, "bar" => 5),
174  *  ));
175  *
176  *  {$array1|@unique}
177  *  {$array2|@unique:"foo"}
178  *  </code>
179  *  <code>
180  *  abc
181  *  123
182  *  </code>
183  *  
184  *  @param  array   $array  処理対象となる配列
185  *  @param  key     $key    処理対象となるキー(nullなら配列要素)
186  *  @return array   再構成された配列
187  */
188 function smarty_modifier_unique($array, $key = null)
189 {
190     if (is_array($array) == false) {
191         return $array;
192     }
193     if ($key != null) {
194         $tmp = array();
195         foreach ($array as $v) {
196             if (isset($v[$key]) == false) {
197                 continue;
198             }
199             $tmp[$v[$key]] = $v;
200         }
201         return $tmp;
202     } else {
203         return array_unique($array);
204     }
205 }
206 // }}}
207
208 // {{{ smarty_modifier_wordwrap_i18n
209 /**
210  *  smarty modifier:文字列のwordwrap処理
211  *
212  *  [現在EUC-JP対応はEUC-JPのみ対応]
213  *
214  *  sample:
215  *  <code>
216  *  {"あいうaえaおaかきaaaくけこ"|wordrap_i18n:8}
217  *  </code>
218  *  <code>
219  *  あいうa
220  *  えaおaか
221  *  きaaaく
222  *  けこ
223  *  </code>
224  *
225  *  @param  string  $string wordwrapする文字列
226  *  @param  string  $break  改行文字
227  *  @param  int     $width  wordwrap幅(半角$width文字でwordwrapする)
228  *  @param  int     $indent インデント幅(半角$indent文字)
229  *  @return string  wordwrap処理された文字列
230  */
231 function smarty_modifier_wordwrap_i18n($string, $width, $break = "\n", $indent = 0)
232 {
233     $r = "";
234     $i = "$break" . str_repeat(" ", $indent);
235     $tmp = $string;
236     do {
237         $n = strpos($tmp, $break);
238         if ($n !== false && $n < $width) {
239             $s = substr($tmp, 0, $n);
240             $r .= $s . $i;
241             $tmp = substr($tmp, strlen($s) + strlen($break));
242             continue;
243         }
244
245         $s = mb_strimwidth($tmp, 0, $width, "", "EUC-JP");
246
247         // EUC-JPのみ対応
248         $n = strlen($s);
249         if ($n >= $width && $tmp{$n} != "" && $tmp{$n} != " ") {
250             while ((ord($s{$n-1}) & 0x80) == 0) {
251                 if ($s{$n-1} == " " || $n == 0) {
252                     break;
253                 }
254                 $n--;
255             }
256         }
257         $s = substr($s, 0, $n);
258
259         $r .= $s . $i;
260         $tmp = substr($tmp, strlen($s));
261     } while (strlen($s) > 0);
262
263     $r = preg_replace('/\s+$/', '', $r);
264
265     return $r;
266 }
267 // }}}
268
269 // {{{ smarty_modifier_truncate_i18n
270 /**
271  *  smarty modifier:文字列切り詰め処理(i18n対応)
272  *
273  *  sample:
274  *  <code>
275  *  {"日本語です"|truncate_i18n:5:"..."}
276  *  </code>
277  *  <code>
278  *  日本...
279  *  </code>
280  *
281  *  @param  int     $len        最大文字幅
282  *  @param  string  $postfix    末尾に付加する文字列
283  */
284 function smarty_modifier_truncate_i18n($string, $len = 80, $postfix = "...")
285 {
286     return mb_strimwidth($string, 0, $len, $postfix);
287 }
288 // }}}
289
290 // {{{ smarty_modifier_i18n
291 /**
292  *  smarty modifier:i18nフィルタ
293  *
294  *  sample:
295  *  <code>
296  *  {"english"|i18n}
297  *  </code>
298  *  <code>
299  *  英語
300  *  </code>
301  *
302  *  @param  string  $string i18n処理対象の文字列
303  *  @return string  ロケールに対応したメッセージ
304  */
305 function smarty_modifier_i18n($string)
306 {
307     $c =& Ethna_Controller::getInstance();
308
309     $i18n =& $c->getI18N();
310
311     return $i18n->get($string);
312 }
313 // }}}
314
315 // {{{ smarty_modifier_checkbox
316 /**
317  *  smarty modifier:チェックボックス用フィルタ
318  *
319  *  sample:
320  *  <code>
321  *  <input type="checkbox" name="test" {""|checkbox}>
322  *  <input type="checkbox" name="test" {"1"|checkbox}>
323  *  </code>
324  *  <code>
325  *  <input type="checkbox" name="test">
326  *  <input type="checkbox" name="test" checkbox>
327  *  </code>
328  *
329  *  @param  string  $string チェックボックスに渡されたフォーム値
330  *  @return string  $stringが空文字列あるいは0以外の場合は"checked"
331  */
332 function smarty_modifier_checkbox($string)
333 {
334     if ($string != "" && $string != 0) {
335         return "checked";
336     }
337 }
338 // }}}
339
340 // {{{ smarty_modifier_select
341 /**
342  *  smarty modifier:セレクトボックス用フィルタ
343  *
344  *  単純なセレクトボックスの場合はsmarty関数"select"を利用することで
345  *  タグを省略可能
346  *
347  *  sample:
348  *  <code>
349  *  $smarty->assign("form", 1);
350  *
351  *  <option value="1" {$form|select:"1"}>foo</option>
352  *  <option value="2" {$form|select:"2"}>bar</option>
353  *  </code>
354  *  <code>
355  *  <option value="1" selected>foo</option>
356  *  <option value="2" >bar</option>
357  *  </code>
358  *
359  *  @param  string  $string セレクトボックスに渡されたフォーム値
360  *  @param  string  $value  <option>タグに指定されている値
361  *  @return string  $stringが$valueにマッチする場合は"selected"
362  */
363 function smarty_modifier_select($string, $value)
364 {
365     if ($string == $value) {
366         return 'selected="true"';
367     }
368 }
369 // }}}
370
371 // {{{ smarty_modifier_form_value
372 /**
373  *  smarty modifier:フォーム値出力フィルタ
374  *
375  *  フォーム名を変数で指定してフォーム値を取得したい場合に使用する
376  *
377  *  sample:
378  *  <code>
379  *  $this->af->set('foo', 'bar);
380  *  $smarty->assign('key', 'foo');
381  *  {$key|form_value}
382  *  </code>
383  *  <code>
384  *  bar
385  *  </code>
386  *
387  *  @param  string  $string フォーム項目名
388  *  @return string  フォーム値
389  */
390 function smarty_modifier_form_value($string)
391 {
392     $c =& Ethna_Controller::getInstance();
393     $af =& $c->getActionForm();
394
395     $elts = explode(".", $string);
396     $r = $af->get($elts[0]);
397     for ($i = 1; $i < count($elts); $i++) {
398         $r = $r[$elts[$i]];
399     }
400
401     return htmlspecialchars($r, ENT_QUOTES);
402 }
403 // }}}
404
405 // {{{ smarty_function_is_error
406 /**
407  *  smarty function:指定されたフォーム項目でエラーが発生しているかどうかを返す
408  *  NOTE: {if is_error('name')} は Ethna_Util.php の is_error() であって、
409  *        smarty_function_is_error() ではないことに注意
410  *
411  *  @param  string  $name   フォーム項目名
412  */
413 function smarty_function_is_error($params, &$smarty)
414 {
415     $name = isset($params['name']) ? $params['name'] : null;
416     return is_error($name);
417 }
418 // }}}
419
420 // {{{ smarty_function_message
421 /**
422  *  smarty function:指定されたフォーム項目に対応するエラーメッセージを出力する
423  *
424  *  sample:
425  *  <code>
426  *  <input type="text" name="foo">{message name="foo"}
427  *  </code>
428  *  <code>
429  *  <input type="text" name="foo">fooを入力してください
430  *  </code>
431  *
432  *  @param  string  $name   フォーム項目名
433  */
434 function smarty_function_message($params, &$smarty)
435 {
436     if (isset($params['name']) === false) {
437         return '';
438     }
439
440     $c =& Ethna_Controller::getInstance();
441     $action_error =& $c->getActionError();
442
443     $message = $action_error->getMessage($params['name']);
444     if ($message === null) {
445         return '';
446     }
447
448     $id = isset($params['id']) ? $params['id']
449         : str_replace("_", "-", "ethna-error-" . $params['name']);
450     $class = isset($params['class']) ? $params['class'] : "ethna-error";
451     return sprintf('<span class="%s" id="%s">%s</span>',
452         $class, $id, htmlspecialchars($message));
453 }
454 // }}}
455
456 // {{{ smarty_function_uniqid
457 /**
458  *  smarty function:ユニークIDを生成する(double postチェック用)
459  *
460  *  sample:
461  *  <code>
462  *  {uniqid}
463  *  </code>
464  *  <code>
465  *  <input type="hidden" name="uniqid" value="a0f24f75e...e48864d3e">
466  *  </code>
467  *
468  *  @param  string  $type   表示タイプ("get" or "post"−デフォルト="post")
469  *  @see    isDuplicatePost
470  */
471 function smarty_function_uniqid($params, &$smarty)
472 {
473     $uniqid = Ethna_Util::getRandom();
474     if (isset($params['type']) && $params['type'] == 'get') {
475         return "uniqid=$uniqid";
476     } else {
477         return "<input type=\"hidden\" name=\"uniqid\" value=\"$uniqid\" />\n";
478     }
479 }
480 // }}}
481
482 // {{{ smarty_function_select
483 /**
484  *  smarty function:セレクトフィールド生成
485  *
486  *  @param  array   $list   選択肢一覧
487  *  @param  string  $name   フォーム項目名
488  *  @param  string  $value  セレクトボックスに渡されたフォーム値
489  *  @param  string  $empty  空エントリ(「---選択して下さい---」等)
490  *  @deprecated
491  */
492 function smarty_function_select($params, &$smarty)
493 {
494     extract($params);
495
496     print "<select name=\"$name\">\n";
497     if ($empty) {
498         printf("<option value=\"\">%s</option>\n", $empty);
499     }
500     foreach ($list as $id => $elt) {
501         printf("<option value=\"%s\" %s>%s</option>\n", $id, $id == $value ? 'selected="true"' : '', $elt['name']);
502     }
503     print "</select>\n";
504 }
505 // }}}
506
507 // {{{ smarty_function_checkbox_list
508 /**
509  *  smarty function:チェックボックスフィルタ関数(配列対応)
510  *
511  *  @param  string  $form   チェックボックスに渡されたフォーム値
512  *  @param  string  $key    評価対象の配列インデックス
513  *  @param  string  $value  評価値
514  *  @deprecated
515  */
516 function smarty_function_checkbox_list($params, &$smarty)
517 {
518     extract($params);
519
520     if (isset($key) == false) {
521         $key = null;
522     }
523     if (isset($value) == false) {
524         $value = null;
525     }
526     if (isset($checked) == false) {
527         $checked = "checked";
528     }
529
530     if (is_null($key) == false) {
531         if (isset($form[$key])) {
532             if (is_null($value)) {
533                 print $checked;
534             } else {
535                 if (strcmp($form[$key], $value) == 0) {
536                     print $checked;
537                 }
538             }
539         }
540     } else if (is_null($value) == false) {
541         if (is_array($form)) {
542             if (in_array($value, $form)) {
543                 print $checked;
544             }
545         } else {
546             if (strcmp($value, $form) == 0) {
547                 print $checked;
548             }
549         }
550     }
551 }
552 // }}}
553
554 // {{{ smarty_function_url
555 /**
556  *  smarty function:url生成
557  */
558 function smarty_function_url($params, &$smarty)
559 {
560     $action = $path = $path_key = null;
561     $query = $params;
562
563     foreach (array('action', 'anchor', 'scheme') as $key) {
564         if (isset($params[$key])) {
565             ${$key} = $params[$key];
566         } else {
567             ${$key} = null;
568         }
569         unset($query[$key]);
570     }
571
572     $c =& Ethna_Controller::getInstance();
573     $config =& $c->getConfig();
574     $url_handler =& $c->getUrlHandler();
575     list($path, $path_key) = $url_handler->actionToRequest($action, $query);
576
577     if ($path != "") {
578         if (is_array($path_key)) {
579             foreach ($path_key as $key) {
580                 unset($query[$key]);
581             }
582         }
583     } else {
584         $query = $url_handler->buildActionParameter($query, $action);
585     }
586     $query = $url_handler->buildQueryParameter($query);
587
588     $url = sprintf('%s%s', $config->get('url'), $path);
589
590     if (preg_match('|^(\w+)://(.*)$|', $url, $match)) {
591         if ($scheme) {
592             $match[1] = $scheme;
593         }
594         $match[2] = preg_replace('|/+|', '/', $match[2]);
595         $url = $match[1] . '://' . $match[2];
596     }
597
598     $url .= $query ? "?$query" : "";
599     $url .= $anchor ? "#$anchor" : "";
600
601     return $url;
602 }
603 // }}}
604
605 // {{{ smarty_function_form_name
606 /**
607  *  smarty function:フォーム表示名生成
608  *
609  *  @param  string  $name   フォーム項目名
610  */
611 function smarty_function_form_name($params, &$smarty)
612 {
613     // name
614     if (isset($params['name'])) {
615         $name = $params['name'];
616         unset($params['name']);
617     } else {
618         return null;
619     }
620
621     // view object
622     $c =& Ethna_Controller::getInstance();
623     $view =& $c->getView();
624     if ($view === null) {
625         return null;
626     }
627
628     // action
629     $action = null;
630     if (isset($params['action'])) {
631         $action = $params['action'];
632         unset($params['action']);
633     } else {
634         for ($i = count($smarty->_tag_stack); $i >= 0; --$i) {
635             if ($smarty->_tag_stack[$i][0] === 'form') {
636                 if (isset($smarty->_tag_stack[$i][1]['ethna_action'])) {
637                     $action = $smarty->_tag_stack[$i][1]['ethna_action'];
638                 }
639                 break;
640             }
641         }
642     }
643     if ($action !== null) {
644         $view->addActionFormHelper($action);
645     }
646
647     return $view->getFormName($name, $action, $params);
648 }
649 // }}}
650
651 // {{{ smarty_function_form_submit
652 /**
653  *  smarty function:フォームのsubmitボタン生成
654  *
655  *  @param  string  $submit   フォーム項目名
656  */
657 function smarty_function_form_submit($params, &$smarty)
658 {
659     $c =& Ethna_Controller::getInstance();
660     $view =& $c->getView();
661     if ($view === null) {
662         return null;
663     }
664     return $view->getFormSubmit($params);
665 }
666 // }}}
667
668 // {{{ smarty_function_form_input
669 /**
670  *  smarty function:フォームタグ生成
671  *
672  *  @param  string  $name   フォーム項目名
673  */
674 function smarty_function_form_input($params, &$smarty)
675 {
676     // name
677     if (isset($params['name'])) {
678         $name = $params['name'];
679         unset($params['name']);
680     } else {
681         return null;
682     }
683
684     // view object
685     $c =& Ethna_Controller::getInstance();
686     $view =& $c->getView();
687     if ($view === null) {
688         return null;
689     }
690
691     // 現在の{form_input}を囲むform blockがあればパラメータを取得しておく
692     $block_params = null;
693     for ($i = count($smarty->_tag_stack); $i >= 0; --$i) {
694         if ($smarty->_tag_stack[$i][0] === 'form') {
695             $block_params = $smarty->_tag_stack[$i][1];
696             break;
697         }
698     }
699
700     // action
701     $action = null;
702     if (isset($params['action'])) {
703         $action = $params['action'];
704         unset($params['action']);
705     } else if (isset($block_params['ethna_action'])) {
706         $action = $block_params['ethna_action'];
707     }
708     if ($action !== null) {
709         $view->addActionFormHelper($action);
710     }
711
712     // default
713     if (isset($params['default'])) {
714         // {form_input default=...}が指定されていればそのまま
715
716     } else if (isset($block_params['default'])) {
717         // 外側の {form default=...} ブロック
718         if (isset($block_params['default'][$name])) {
719             $params['default'] = $block_params['default'][$name];
720         }
721     }
722
723     // 現在のアクションで受け取ったフォーム値
724     $af =& $c->getActionForm();
725     $val = $af->get($name);
726     if ($val !== null) {
727         $params['default'] = $val;
728     }
729
730     return $view->getFormInput($name, $action, $params);
731 }
732 // }}}
733
734 // {{{ smarty_block_form
735 /**
736  *  smarty block:フォームタグ出力プラグイン
737  */
738 function smarty_block_form($params, $content, &$smarty, &$repeat)
739 {
740     if ($repeat) {
741         // {form}: ブロック内部に進む前の処理
742
743         // default
744         if (isset($params['default']) === false) {
745             // 指定なしのときは $form を使う
746             $c =& Ethna_Controller::getInstance();
747             $af =& $c->getActionForm();
748
749             // c.f. http://smarty.php.net/manual/en/plugins.block.functions.php
750             $smarty->_tag_stack[count($smarty->_tag_stack)-1][1]['default']
751                 =& $af->getArray(false);
752         }
753
754         // ここで返す値は出力されない
755         return '';
756
757     } else {
758         // {/form}: ブロック全体を出力
759
760         $c =& Ethna_Controller::getInstance();
761         $view =& $c->getView();
762         if ($view === null) {
763             return null;
764         }
765
766         // ethna_action
767         if (isset($params['ethna_action'])) {
768             $ethna_action = $params['ethna_action'];
769             unset($params['ethna_action']);
770
771             $view->addActionFormHelper($ethna_action);
772             $hidden = $c->getActionRequest($ethna_action, 'hidden');
773             $content = $hidden . $content;
774         }
775
776         // enctype の略称対応
777         if (isset($params['enctype'])) {
778             if ($params['enctype'] == 'file'
779                 || $params['enctype'] == 'multipart') {
780                 $params['enctype'] = 'multipart/form-data';
781             } else if ($params['enctype'] == 'url') {
782                 $params['enctype'] = 'application/x-www-form-urlencoded';
783             }
784         }
785
786         // defaultはもう不要
787         if (isset($params['default'])) {
788             unset($params['default']);
789         }
790
791         // $contentを囲む<form>ブロック全体を出力
792         return $view->getFormBlock($content, $params);
793     }
794 }
795 // }}}
796
797 // {{{ smarty_function_csrfid
798 /**
799  *  smarty function: 正当なポストであることを保証するIDを出力する
800  *
801  *  sample:
802  *  <code>
803  *  {csrfid}
804  *  </code>
805  *  <code>
806  *  <input type="hidden" name="csrfid" value="a0f24f75e...e48864d3e">
807  *  </code>
808  *
809  *  @param  string  $type   表示タイプ("get" or "post"−デフォルト="post")
810  *  @see    isRequestValid
811  */
812 function smarty_function_csrfid($params, &$smarty)
813 {
814     $c =& Ethna_Controller::getInstance();
815     $name = $c->config->get('csrf');
816     if (is_null($name)) {
817         $name = 'Session';
818     }
819     $plugin =& $c->getPlugin();
820     $csrf = $plugin->getPlugin('Csrf', $name);
821     $csrfid = $csrf->get();
822     $token_name = $csrf->getName();
823     if (isset($params['type']) && $params['type'] == 'get') {
824         return sprintf("%s=%s", $token_name, $csrfid);
825     } else {
826         return sprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n", $token_name, $csrfid);
827     }
828 }
829 // }}}
830
831 ?>