runo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +120 -0
  3. data/bin/runo +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +141 -0
  7. data/lib/_parser.rb +243 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +213 -0
  10. data/lib/_storage/file.rb +200 -0
  11. data/lib/_storage/sequel.rb +174 -0
  12. data/lib/_storage/temp.rb +73 -0
  13. data/lib/_widget/action_create.rb +23 -0
  14. data/lib/_widget/action_login.rb +22 -0
  15. data/lib/_widget/action_signup.rb +16 -0
  16. data/lib/_widget/action_update.rb +16 -0
  17. data/lib/_widget/crumb.rb +24 -0
  18. data/lib/_widget/done.rb +16 -0
  19. data/lib/_widget/login.rb +25 -0
  20. data/lib/_widget/me.rb +31 -0
  21. data/lib/_widget/message.rb +51 -0
  22. data/lib/_widget/navi.rb +88 -0
  23. data/lib/_widget/submit.rb +49 -0
  24. data/lib/_widget/view_ym.rb +77 -0
  25. data/lib/_workflow/_workflow.rb +89 -0
  26. data/lib/_workflow/attachment.rb +50 -0
  27. data/lib/_workflow/blog.rb +28 -0
  28. data/lib/_workflow/contact.rb +23 -0
  29. data/lib/_workflow/forum.rb +26 -0
  30. data/lib/_workflow/register.rb +39 -0
  31. data/lib/meta/_meta.rb +20 -0
  32. data/lib/meta/group.rb +19 -0
  33. data/lib/meta/id.rb +59 -0
  34. data/lib/meta/owner.rb +21 -0
  35. data/lib/meta/timestamp.rb +118 -0
  36. data/lib/runo.rb +396 -0
  37. data/lib/scalar/checkbox.rb +68 -0
  38. data/lib/scalar/file.rb +144 -0
  39. data/lib/scalar/img.rb +112 -0
  40. data/lib/scalar/password.rb +58 -0
  41. data/lib/scalar/radio.rb +47 -0
  42. data/lib/scalar/select.rb +47 -0
  43. data/lib/scalar/text.rb +38 -0
  44. data/lib/scalar/textarea.rb +35 -0
  45. data/lib/scalar/textarea_pre.rb +14 -0
  46. data/lib/scalar/textarea_wiki.rb +173 -0
  47. data/lib/set/_set.rb +195 -0
  48. data/lib/set/dynamic.rb +177 -0
  49. data/lib/set/static.rb +102 -0
  50. data/lib/set/static_folder.rb +96 -0
  51. data/locale/en/index.po +242 -0
  52. data/locale/index.pot +243 -0
  53. data/locale/ja/index.po +242 -0
  54. data/locale/lazy_parser.rb +54 -0
  55. data/skel/config.ru +27 -0
  56. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  57. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank.yaml +12 -0
  59. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  60. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  61. data/skel/skin/_users/00000000_root.yaml +11 -0
  62. data/skel/skin/_users/css/users.css +21 -0
  63. data/skel/skin/_users/css/users.less +25 -0
  64. data/skel/skin/_users/done.html +42 -0
  65. data/skel/skin/_users/index.html +46 -0
  66. data/skel/skin/_users/index.yaml +3 -0
  67. data/skel/skin/_users/summary.html +40 -0
  68. data/skel/skin/css/base.css +93 -0
  69. data/skel/skin/css/base.less +139 -0
  70. data/skel/skin/css/coax.css +199 -0
  71. data/skel/skin/css/coax.less +244 -0
  72. data/skel/skin/examples/blog/20091214_0001.yaml +8 -0
  73. data/skel/skin/examples/blog/20100630_0001.yaml +8 -0
  74. data/skel/skin/examples/blog/20100630_0002.yaml +14 -0
  75. data/skel/skin/examples/blog/20100701_0001.yaml +8 -0
  76. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002.yaml +19 -0
  79. data/skel/skin/examples/blog/frank/20100701_0001.yaml +10 -0
  80. data/skel/skin/examples/blog/frank/index.yaml +4 -0
  81. data/skel/skin/examples/blog/index.html +51 -0
  82. data/skel/skin/examples/blog/rss.xml +18 -0
  83. data/skel/skin/examples/contact/20100701_0001-file.txt +1 -0
  84. data/skel/skin/examples/contact/20100701_0001.yaml +15 -0
  85. data/skel/skin/examples/contact/20100701_0002.yaml +8 -0
  86. data/skel/skin/examples/contact/20100701_0003.yaml +9 -0
  87. data/skel/skin/examples/contact/index.html +47 -0
  88. data/skel/skin/examples/contact/js/contact.js +13 -0
  89. data/skel/skin/examples/contact/summary.html +54 -0
  90. data/skel/skin/examples/forum/20100701_0001.yaml +41 -0
  91. data/skel/skin/examples/forum/20100701_0002.yaml +25 -0
  92. data/skel/skin/examples/forum/index.html +68 -0
  93. data/skel/skin/examples/forum/summary.html +47 -0
  94. data/skel/skin/examples/index.html +75 -0
  95. data/skel/skin/index.html +41 -0
  96. data/skel/skin/js/base.js +50 -0
  97. data/t/locale/de/index.po +19 -0
  98. data/t/locale/en-GB/index.po +25 -0
  99. data/t/locale/ja/index.po +30 -0
  100. data/t/skin/_users/00000000_test.yaml +3 -0
  101. data/t/skin/_users/index.html +13 -0
  102. data/t/skin/foo/20091120_0001.yaml +7 -0
  103. data/t/skin/foo/bar/20091120_0001.yaml +5 -0
  104. data/t/skin/foo/bar/index.yaml +5 -0
  105. data/t/skin/foo/baz/css/baz.css +1 -0
  106. data/t/skin/foo/css/foo.css +1 -0
  107. data/t/skin/foo/index.html +14 -0
  108. data/t/skin/foo/index.yaml +7 -0
  109. data/t/skin/foo/not_css/foo.css +1 -0
  110. data/t/skin/foo/sub-20100306_0001.yaml +3 -0
  111. data/t/skin/index.yaml +3 -0
  112. data/t/skin/t_attachment/index.html +13 -0
  113. data/t/skin/t_contact/done.html +6 -0
  114. data/t/skin/t_contact/index.html +9 -0
  115. data/t/skin/t_file/index.html +16 -0
  116. data/t/skin/t_img/index.html +14 -0
  117. data/t/skin/t_img/test.jpg +0 -0
  118. data/t/skin/t_select/index.html +9 -0
  119. data/t/skin/t_store/index.html +9 -0
  120. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  121. data/t/skin/t_summary/create.html +9 -0
  122. data/t/skin/t_summary/index.html +9 -0
  123. data/t/skin/t_summary/summary.html +9 -0
  124. data/t/t.rb +27 -0
  125. data/t/test_checkbox.rb +273 -0
  126. data/t/test_field.rb +330 -0
  127. data/t/test_file.rb +900 -0
  128. data/t/test_id.rb +215 -0
  129. data/t/test_img.rb +328 -0
  130. data/t/test_meta.rb +57 -0
  131. data/t/test_parser.rb +1266 -0
  132. data/t/test_password.rb +188 -0
  133. data/t/test_radio.rb +226 -0
  134. data/t/test_role.rb +249 -0
  135. data/t/test_runo.rb +742 -0
  136. data/t/test_runo_call.rb +1286 -0
  137. data/t/test_runo_i18n.rb +318 -0
  138. data/t/test_select.rb +182 -0
  139. data/t/test_set_complex.rb +527 -0
  140. data/t/test_set_dynamic.rb +1504 -0
  141. data/t/test_set_folder.rb +515 -0
  142. data/t/test_set_permit.rb +246 -0
  143. data/t/test_set_static.rb +445 -0
  144. data/t/test_storage.rb +915 -0
  145. data/t/test_text.rb +125 -0
  146. data/t/test_textarea.rb +138 -0
  147. data/t/test_timestamp.rb +473 -0
  148. data/t/test_workflow.rb +367 -0
  149. metadata +345 -0
@@ -0,0 +1,73 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Runo::Storage::Temp < Runo::Storage
7
+
8
+ def self.available?
9
+ true
10
+ end
11
+
12
+ def persistent?
13
+ false
14
+ end
15
+
16
+ def val(id = nil)
17
+ @val ||= {}
18
+ id ? @val[id] : @val
19
+ end
20
+
21
+ def build(v)
22
+ @val = v
23
+ self
24
+ end
25
+
26
+ def clear
27
+ @val = {}
28
+ self
29
+ end
30
+
31
+ def store(id, v, ext = :unused)
32
+ if new_id?(id, v)
33
+ old_id = id
34
+ id = new_id v
35
+ return if val[id] # duplicate id
36
+ move(old_id, id) unless old_id == :new_id
37
+ end
38
+ val[id] = v
39
+ id
40
+ end
41
+
42
+ def delete(id)
43
+ @val.delete id
44
+ id
45
+ end
46
+
47
+ def move(old_id, new_id)
48
+ rex = /\A#{old_id}/
49
+ val.keys.each {|id|
50
+ if id =~ rex
51
+ to_id = id.sub(rex, new_id)
52
+ @val[to_id] = @val.delete id
53
+ end
54
+ }
55
+ new_id
56
+ end
57
+
58
+ private
59
+
60
+ def _select_by_id(conds)
61
+ val.keys & conds[:id].to_a
62
+ end
63
+
64
+ def _select_by_d(conds)
65
+ rex_d = /^#{conds[:d]}/
66
+ val.keys.select {|id| id[rex_d] }
67
+ end
68
+
69
+ def _select_all(conds)
70
+ val.keys
71
+ end
72
+
73
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Dynamic
7
+
8
+ private
9
+
10
+ def _g_action_create(arg)
11
+ label = _('create new %{item}...') % {
12
+ :item => _((my[:item].size == 1 && my[:item]['default'][:label]) || my[:item_label])
13
+ }
14
+ (_get_by_action_tmpl(arg) || <<_html) if permit? :create
15
+ <div class="action_create"><a href="#{_g_uri_create arg}">#{label}</a></div>
16
+ _html
17
+ end
18
+
19
+ def _g_uri_create(arg)
20
+ "#{my[:path]}/create.html"
21
+ end
22
+
23
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Static::Folder
7
+
8
+ private
9
+
10
+ def _g_action_login(arg)
11
+ if Runo.client == 'nobody'
12
+ <<_html
13
+ <div class="action_login"><a href="#{my[:dir]}/login.html">#{_ 'login'}</a></div>
14
+ _html
15
+ else
16
+ <<_html
17
+ <div class="action_logout"><a href="#{my[:dir]}/logout.html?_token=#{Runo.token}">#{_ 'logout'}</a></div>
18
+ _html
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Static::Folder
7
+
8
+ private
9
+
10
+ def _g_action_signup(arg)
11
+ (_get_by_action_tmpl(arg) || <<_html) if Runo.client == 'nobody'
12
+ <div class="action_signup"><a href="/_users/create.html">#{_ 'signup'}</a></div>
13
+ _html
14
+ end
15
+
16
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Static
7
+
8
+ private
9
+
10
+ def _g_action_update(arg)
11
+ (_get_by_action_tmpl(arg) || <<_html.chomp) if permit_get?(:action => :update)
12
+ <div class="action_update"><a href="#{_g_uri_update arg}">#{_ 'update...'}</a></div>
13
+ _html
14
+ end
15
+
16
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Static::Folder
7
+
8
+ private
9
+
10
+ def _g_crumb(arg)
11
+ <<_html
12
+ <div class="crumb">
13
+ #{_crumb}</div>
14
+ _html
15
+ end
16
+
17
+ def _crumb
18
+ crumb = <<_html
19
+ <a href="#{Runo.uri}#{my[:dir]}/">#{my[:label] || my[:id]}</a>
20
+ _html
21
+ my[:parent] ? [my[:parent].send(:_crumb), crumb].join(" &raquo;\n") : crumb
22
+ end
23
+
24
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Dynamic
7
+
8
+ private
9
+
10
+ def _g_done(arg)
11
+ (_get_by_action_tmpl(arg) || <<_html.chomp) if arg[:orig_action] == :done
12
+ <div class="done">#{_ 'done.'}</div>
13
+ _html
14
+ end
15
+
16
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Dynamic
7
+
8
+ private
9
+
10
+ def _g_login(arg)
11
+ path = Runo::Path.path_of arg[:conds]
12
+ action = arg[:dest_action]
13
+ <<_html
14
+ <form id="form_#{my[:name]}" method="post" action="#{my[:base_path]}/#{path}login.html">
15
+ <div class="login">
16
+ <input type="hidden" name="dest_action" value="#{action}" />
17
+ <label for="login_id">#{_ 'ID'}</label><input type="text" id="login_id" name="id" size="10" value="" />
18
+ <label for="login_pw">#{_ 'Password'}</label><input type="password" id="login_pw" name="pw" size="10" value="" />
19
+ <input type="submit" value="#{_ 'login'}" />
20
+ </div>
21
+ </form>
22
+ _html
23
+ end
24
+
25
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Static::Folder
7
+
8
+ private
9
+
10
+ def _g_me(arg)
11
+ if Runo.client == 'nobody'
12
+ <<_html
13
+ <div class="me">
14
+ #{_g_action_login arg}</div>
15
+ _html
16
+ else
17
+ img = Runo::Set::Static::Folder.root.item('_users', 'main', Runo.client, 'avatar')
18
+ roles = Runo::Workflow.roles my[:roles]
19
+ <<_html
20
+ <div class="me">
21
+ <a href="#{Runo.uri}/_users/id=#{Runo.client}/update.html">
22
+ #{img.send(:_get_by_self_reference, :sub_action => :without_link) if img}
23
+ </a>
24
+ <div class="client">#{Runo.client}</div>
25
+ <div class="roles">(#{roles.join ', '})</div>
26
+ #{_g_action_login arg}</div>
27
+ _html
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Dynamic
7
+
8
+ private
9
+
10
+ def _g_message(arg)
11
+ return if arg[:orig_action] == :done && my[:tmpl][:done]
12
+ return unless self == Runo.base
13
+
14
+ if arg[:dest_action]
15
+ message = {:alert => _('please login.')}
16
+ elsif arg[:orig_action] == :preview
17
+ message = {:notice => _('please confirm.')}
18
+ elsif !self.valid? && arg[:orig_action] != :create
19
+ message = {:error => _('malformed input.')}
20
+ elsif Runo.transaction[my[:tid]].is_a? ::Hash
21
+ message = {
22
+ :notice => Runo.transaction[my[:tid]].keys.collect {|item_result|
23
+ n = Runo.transaction[my[:tid]][item_result]
24
+ _('%{result} %{n} %{item}.') % {
25
+ :result => {
26
+ :create => _('created'),
27
+ :update => _('updated'),
28
+ :delete => _('deleted'),
29
+ }[item_result],
30
+ :n => n,
31
+ :item => n_(
32
+ (my[:item].size == 1 && my[:item]['default'][:label]) || my[:item_label],
33
+ '',
34
+ n
35
+ )
36
+ }
37
+ }
38
+ } unless Runo.transaction[my[:tid]].empty?
39
+ Runo.transaction[my[:tid]] = :expired
40
+ end
41
+
42
+ message.keys.collect {|type|
43
+ lis = message[type].collect {|m| " <li>#{Runo::Field.h m}</li>\n" }
44
+ <<_html
45
+ <ul class="message #{type}">
46
+ #{lis}</ul>
47
+ _html
48
+ }.join if message
49
+ end
50
+
51
+ end
@@ -0,0 +1,88 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Dynamic
7
+
8
+ private
9
+
10
+ def _g_navi(arg)
11
+ arg[:navi] ||= @storage.navi(arg[:conds] || {})
12
+ return unless (arg[:orig_action] == :read) && (arg[:navi][:prev] || arg[:navi][:next])
13
+
14
+ div = my[:tmpl][:navi] || '<div class="navi">$(.navi_prev) | $(.navi_p)$(.navi_next)</div>'
15
+ div.gsub(/\$\(\.(navi_prev|navi_next|navi_p|uri_prev|uri_next)\)/) {
16
+ __send__("_g_#{$1}", arg)
17
+ }
18
+ end
19
+
20
+ def _g_navi_prev(arg)
21
+ button = my[:tmpl][:navi_prev] || _('prev')
22
+ (uri = _g_uri_prev(arg)) ? "<a href=\"#{my[:path]}/#{uri}\">#{button}</a>" : button
23
+ end
24
+
25
+ def _g_navi_next(arg)
26
+ button = my[:tmpl][:navi_next] || _('next')
27
+ (uri = _g_uri_next(arg)) ? "<a href=\"#{my[:path]}/#{uri}\">#{button}</a>" : button
28
+ end
29
+
30
+ def _g_navi_p(arg)
31
+ uris = _uri_p(arg)
32
+ return unless uris && uris.size > 1
33
+
34
+ item_tmpl = nil
35
+ div = my[:tmpl][:navi_p] || '<span class="item">$() </span> | '
36
+ div = Runo::Parser.gsub_block(div, 'item') {|open, inner, close|
37
+ item_tmpl = open + inner + close
38
+ '$(.items)'
39
+ }
40
+ div.gsub('$(.items)') {
41
+ uris.collect {|uri|
42
+ p = uri[/p=(\d+)/, 1] || '1'
43
+ if arg[:conds][:p] == p
44
+ item_tmpl.gsub('$()', p)
45
+ else
46
+ item_tmpl.gsub('$()', "<a href=\"#{my[:path]}/#{uri}\">#{p}</a>")
47
+ end
48
+ }.join
49
+ }
50
+ end
51
+
52
+ def _g_uri_prev(arg)
53
+ arg[:navi] ||= @storage.navi(arg[:conds] || {})
54
+ Runo::Path.path_of(arg[:navi][:prev]) + _uri_action(arg) if arg[:navi][:prev]
55
+ end
56
+
57
+ def _g_uri_next(arg)
58
+ arg[:navi] ||= @storage.navi(arg[:conds] || {})
59
+ Runo::Path.path_of(arg[:navi][:next]) + _uri_action(arg) if arg[:navi][:next]
60
+ end
61
+
62
+ def _uri_p(arg)
63
+ arg[:navi] ||= @storage.navi(arg[:conds] || {})
64
+ if arg[:navi][:sibs] && arg[:navi][:sibs].keys.first == :p
65
+ base_conds = arg[:conds].dup
66
+ base_conds.delete :p
67
+ conds = arg[:navi][:sibs].values.first
68
+ if p = arg[:conds][:p]
69
+ range = ['1', conds.last] + ((p.to_i - 5)..(p.to_i + 5)).to_a.collect {|i| i.to_s }
70
+ conds = conds & range
71
+ end
72
+ conds.collect {|cond|
73
+ Runo::Path.path_of(base_conds.merge(:p => cond)) + _uri_action(arg)
74
+ }
75
+ end
76
+ end
77
+
78
+ def _uri_action(arg)
79
+ if [nil, default_action].include?(arg[:orig_action]) && !arg[:sub_action]
80
+ ''
81
+ elsif arg[:sub_action]
82
+ "#{arg[:orig_action]}_#{arg[:sub_action]}.html"
83
+ else
84
+ "#{arg[:orig_action]}.html"
85
+ end
86
+ end
87
+
88
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ class Runo::Set::Dynamic
7
+
8
+ private
9
+
10
+ def _g_submit(arg)
11
+ <<_html
12
+ <div class="submit">
13
+ #{_g_submit_commit arg}#{_g_submit_preview arg}#{_g_submit_preview_delete arg}</div>
14
+ _html
15
+ end
16
+
17
+ def _g_submit_commit(arg)
18
+ if collect_item(arg[:conds]).all? {|i| i[:id] =~ Runo::REX::ID_NEW }
19
+ action = :create
20
+ elsif arg[:orig_action] == :preview
21
+ action = arg[:sub_action]
22
+ else
23
+ action = arg[:orig_action]
24
+ end
25
+ <<_html unless my[:preview] == :mandatory && arg[:orig_action] != :preview
26
+ <input name="#{my[:short_name]}.status-public" type="submit" value="#{_ action.to_s}" />
27
+ _html
28
+ end
29
+
30
+ def _g_submit_preview(arg)
31
+ label = _ 'preview'
32
+ <<_html if my[:preview] && arg[:orig_action] != :preview
33
+ <input name="#{my[:short_name]}.action-preview_#{arg[:orig_action]}" type="submit" value="#{label}" />
34
+ _html
35
+ end
36
+
37
+ def _g_submit_preview_delete(arg)
38
+ if (
39
+ permit_get?(arg.merge :action => :delete) &&
40
+ collect_item(arg[:conds]).find {|item| item[:id] !~ Runo::REX::ID_NEW } &&
41
+ arg[:orig_action] != :preview
42
+ )
43
+ <<_html
44
+ <input name="#{my[:short_name]}.action-preview_delete" type="submit" value="#{_ 'delete...'}" />
45
+ _html
46
+ end
47
+ end
48
+
49
+ end