microengine 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/LICENSE +674 -0
  2. data/README +102 -0
  3. data/Rakefile +67 -0
  4. data/access.log +1 -0
  5. data/bin/microengine +0 -0
  6. data/content/403/en.body +2 -0
  7. data/content/403/en.header +1 -0
  8. data/content/403/layout +1 -0
  9. data/content/403/ru.body +2 -0
  10. data/content/403/ru.header +1 -0
  11. data/content/404/en.body +5 -0
  12. data/content/404/en.header +1 -0
  13. data/content/404/layout +1 -0
  14. data/content/404/ru.body +5 -0
  15. data/content/404/ru.header +1 -0
  16. data/content/WARNING +2 -0
  17. data/content/en.body +2 -0
  18. data/content/en.header +1 -0
  19. data/content/layout +1 -0
  20. data/content/ru.body +2 -0
  21. data/content/ru.header +1 -0
  22. data/layouts/WARNING +2 -0
  23. data/layouts/default/en.yaml +7 -0
  24. data/layouts/default/layout.rhtml +18 -0
  25. data/layouts/default/ru.yaml +7 -0
  26. data/lib/microengine/admin.rb +268 -0
  27. data/lib/microengine/admin_exception.rb +31 -0
  28. data/lib/microengine/admin_page.rb +199 -0
  29. data/lib/microengine/assambler.rb +192 -0
  30. data/lib/microengine/dispatcher.rb +125 -0
  31. data/lib/microengine/file_cache.rb +62 -0
  32. data/lib/microengine/html/deleter.rhtml +11 -0
  33. data/lib/microengine/html/editor.rhtml +34 -0
  34. data/lib/microengine/html/en.yaml +33 -0
  35. data/lib/microengine/html/error.rhtml +6 -0
  36. data/lib/microengine/html/refresher.rhtml +7 -0
  37. data/lib/microengine/html/ru.yaml +33 -0
  38. data/lib/microengine/html/untranslater.rhtml +11 -0
  39. data/lib/microengine/http.rb +213 -0
  40. data/lib/microengine/initializer.rb +93 -0
  41. data/lib/microengine/memory_cache.rb +53 -0
  42. data/password +20 -0
  43. data/public/microengine.fcgi +5 -0
  44. data/public/styles/handheld.css +0 -0
  45. data/public/styles/print.css +3 -0
  46. data/public/styles/screen.css +18 -0
  47. metadata +106 -0
@@ -0,0 +1,62 @@
1
+ =begin
2
+ Load cached pages directly from files.
3
+ Copyright (C) 2008 Andrey "A.I." Sitnik <andrey@sitnik.ru>
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ module Microengine
20
+ # Load cached pages directly from files. MemoryCache is more faster.
21
+ class FileCache
22
+ # Available languages
23
+ attr_reader :langs
24
+
25
+ # Refresh cache
26
+ def refresh
27
+ @langs = {}
28
+ cache '/'
29
+ end
30
+
31
+ # Return XHTML page content
32
+ def get(path, lang)
33
+ lang.sub!(/[^[:alpha:]_-]/, '')
34
+ IO.read MICROENGINE_ROOT + '/cache/' + path + 'content.' + lang + '.html'
35
+ end
36
+
37
+ protected
38
+
39
+ # Load to memory neccasary resourses
40
+ def cache(dir)
41
+ langs = []
42
+ path = MICROENGINE_ROOT + '/cache/' + dir
43
+ Dir.foreach path do |entry|
44
+ if '.' == entry[0..0]
45
+ next
46
+ elsif File.directory? path + entry + '/'
47
+ cache dir + entry + '/'
48
+ elsif 'content.' == entry[0..7] and '.html' == entry[-5..-1]
49
+ langs.push entry[8..-6]
50
+ end
51
+ end
52
+ if not langs.empty?
53
+ cache_content dir, langs
54
+ end
55
+ end
56
+
57
+ # Cache directory languages
58
+ def cache_content(dir, langs)
59
+ @langs[dir] = langs
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ <form id="admin-deleter" method="post" action="/admin/do/delete<%= @path %>">
2
+ <input type="hidden" name="lang" value="<%= @lang %>" />
3
+ <div>
4
+ <% if @show_password %>
5
+ <%= @translation['password'] %>:
6
+ <input type="password" name="password" />
7
+ <% end %>
8
+ <input type="submit" value="<%= @translation['deleter']['delete'] %>" />
9
+ </div>
10
+ </form>
11
+ <%= @body %>
@@ -0,0 +1,34 @@
1
+ <form id="admin-editor" method="post" action="/admin/do/save<%= @path %>">
2
+ <input type="hidden" name="lang" value="<%= @lang %>" />
3
+ <div>
4
+ <%= @translation['editor']['layout'] %>:
5
+ <select name="layout">
6
+ <% @layouts.each do |layout| %>
7
+ <% if @layout == layout %>
8
+ <option selected="selected"><%= layout %></option>
9
+ <% elsif @untranslated_layouts.include? layout %>
10
+ <option disabled="disabled">
11
+ <%= layout %>
12
+ (<%= @translation['editor']['untranslated'].sub('%langs', @untranslated_layouts[layout].join(', ')) %>)
13
+ </option>
14
+ <% else %>
15
+ <option><%= layout %></option>
16
+ <% end %>
17
+ <% end %>
18
+ </select>
19
+ </div>
20
+ <div>
21
+ <%= @translation['editor']['header'] %>:
22
+ <textarea name="header" rows="3" style="width: 100%;"><%= @header %></textarea>
23
+ </div>
24
+ <div>
25
+ <textarea name="body" rows="40" style="width: 100%"><%= @body %></textarea>
26
+ </div>
27
+ <div style="margin-top: 5px;">
28
+ <% if @show_password %>
29
+ <%= @translation['password'] %>:
30
+ <input type="password" name="password" />
31
+ <% end %>
32
+ <input type="submit" value="<%= @translation['editor']['save'] %>" />
33
+ </div>
34
+ </form>
@@ -0,0 +1,33 @@
1
+ # English global translation
2
+
3
+ password: Password
4
+
5
+ error:
6
+ title: Error
7
+ untranslated_layout: Layout %layout isn't translated to %lang.
8
+ no_lang: Variable 'lang' with page language wasn't sended.
9
+ no_body: Variable 'body' with page content wasn't sended.
10
+ no_header: Variable 'header' with page header wasn't sended.
11
+ admin_subdir: You can't edit /admin/ subdirs.
12
+ cant_write: Microengine can't change files. Ensure, that all files in /content/ is chmod 0666.
13
+ delete_system: This is system page and you can't deleted it.
14
+ cant_cache: Cache wasn't refreshed. See system log for details.
15
+
16
+ editor:
17
+ title: Edit
18
+ header: Header
19
+ layout: Layout
20
+ untranslated: Untranslated on %langs
21
+ save: Save
22
+
23
+ untranslater:
24
+ title: Delete translation
25
+ delete: Delete translation
26
+
27
+ deleter:
28
+ title: Delete page
29
+ delete: Delete page and all it's translations
30
+
31
+ refresher:
32
+ title: Refresh cache
33
+ refresh: Refresh page cache
@@ -0,0 +1,6 @@
1
+ <h1><%= @translation['error']['title'] %></h1>
2
+ <% if 'untranslated_layout' == @code %>
3
+ <p><%= @translation['error'][@code].sub('%layout', @layout).sub('%lang', @old_lang) %></p>
4
+ <% else %>
5
+ <p><%= @translation['error'][@code] %></p>
6
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <form id="admin-refresher" method="post" action="/admin/do/refresh/">
2
+ <% if @show_password %>
3
+ <%= @translation['password'] %>:
4
+ <input type="password" name="password" />
5
+ <% end %>
6
+ <input type="submit" value="<%= @translation['refresher']['refresh'] %>" />
7
+ </form>
@@ -0,0 +1,33 @@
1
+ # Russian global translation
2
+
3
+ password: Пароль
4
+
5
+ error:
6
+ title: Ошибка
7
+ untranslated_layout: Шаблон %layout не переведён на %lang.
8
+ no_lang: Не передана переменная lang с языком страницы.
9
+ no_body: Не передана переменная body с содержимым страницы.
10
+ no_header: Не передана переменная header с заголовком страницы.
11
+ admin_subdir: Вы не можете изменять подкаталоги /admin/ .
12
+ cant_write: Microengine не может изменить файл. Убедитесь, что все файлы в /content/ имеют права доступа 0666.
13
+ delete_system: Это системная страница и вы не можете её удалить.
14
+ cant_cache: Кеш не был обновлён. Смотрите детали в системном журнале.
15
+
16
+ editor:
17
+ title: Правка
18
+ header: Заголовок
19
+ layout: Шаблон
20
+ untranslated: Непереведён на %langs
21
+ save: Сохранить
22
+
23
+ untranslater:
24
+ title: Удаление перевода
25
+ delete: Удалить перевод
26
+
27
+ deleter:
28
+ title: Удаление страницы
29
+ delete: Удалить страницу и все её переводы
30
+
31
+ refresher:
32
+ title: Обновление кеша
33
+ refresh: Обновить кеш
@@ -0,0 +1,11 @@
1
+ <form id="admin-untranslater" method="post" action="/admin/do/untranslate<%= @path %>">
2
+ <input type="hidden" name="lang" value="<%= @lang %>" />
3
+ <div>
4
+ <% if @show_password %>
5
+ <%= @translation['password'] %>:
6
+ <input type="password" name="password" />
7
+ <% end %>
8
+ <input type="submit" value="<%= @translation['untranslater']['delete'] %>" />
9
+ </div>
10
+ </form>
11
+ <%= @body %>
@@ -0,0 +1,213 @@
1
+ =begin
2
+ API to easy work with HTTP.
3
+ Copyright (C) 2008 Andrey "A.I." Sitnik <andrey@sitnik.ru>
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require 'cgi'
20
+
21
+ module Microengine
22
+ # Basic operation with HTTP request
23
+ class HTTP
24
+ attr_reader :env
25
+ attr_reader :url
26
+ attr_reader :post
27
+ attr_reader :langs
28
+ attr_reader :agent
29
+ attr_reader :ip
30
+ attr_reader :cookie
31
+ attr_reader :get
32
+ attr_accessor :status
33
+ attr_accessor :content
34
+
35
+ def initialize(request)
36
+ @out = request.out
37
+ @env = request.env
38
+ @agent = request.env['HTTP_USER_AGENT']
39
+ @ip = request.env['REMOTE_ADDR']
40
+ @langs = format_langs request.env['HTTP_ACCEPT_LANGUAGE']
41
+ @url = normalize_url format_url(request.env['REQUEST_URI'])
42
+ @get = format_get request.env['QUERY_STRING']
43
+ @cookie = format_cookie request.env['HTTP_COOKIE']
44
+ @headers = []
45
+ @status = '200 OK'
46
+ @content = 'text/html'
47
+ @request = request
48
+ end
49
+
50
+ def post
51
+ if @post.nil?
52
+ @post = format_post @request.in
53
+ end
54
+ @post
55
+ end
56
+
57
+ # Is URL start with string
58
+ def url_start?(string)
59
+ @url[0...string.length] == string
60
+ end
61
+
62
+ # Is HTTP headers already sended to user
63
+ def header_sended?
64
+ @header_sended
65
+ end
66
+
67
+ # Send content to user
68
+ def << (content)
69
+ if not header_sended?
70
+ send_headers
71
+ end
72
+ @out.print content
73
+ end
74
+
75
+ # Redirect to another URL
76
+ def redirect(url)
77
+ @status = '301 Moved Permanently'
78
+ if '/' == url[0]
79
+ url = 'http://' + @env['HTTP_HOST'] + url
80
+ end
81
+ header 'Location', url
82
+ send_headers
83
+ finish
84
+ end
85
+
86
+ # Set header
87
+ def header(name, value)
88
+ if header_sended?
89
+ Initializer.logger.warning 'Header already sended'
90
+ end
91
+ @headers << [name, value]
92
+ end
93
+
94
+ # Set cookie
95
+ def set_cookie(name, value, days=nil)
96
+ cookie = name + '=' + value + '; path=/'
97
+ if not days.nil?
98
+ time = Time.now + (days * 86400)
99
+ cookie += '; expires=' + time.gmtime.strftime('%a %d-%b-%Y %H:%M:S %Z')
100
+ end
101
+ header 'Set-Cookie', cookie
102
+ @cookie[name] = value
103
+ end
104
+
105
+ # Finish HTTP request
106
+ def finish
107
+ @request.finish
108
+ end
109
+
110
+ private
111
+
112
+ # Format accept languages from string to array
113
+ def format_langs(langs)
114
+ if langs.nil? or '' == langs
115
+ return []
116
+ end
117
+ langs = langs.split(',')
118
+ langs.map! do |lang|
119
+ index = lang.index(';')
120
+ if not index.nil?
121
+ lang[0...index]
122
+ else
123
+ lang
124
+ end
125
+ end
126
+ end
127
+
128
+ # Format GET variables in URI as array
129
+ def format_get(query)
130
+ if query.empty?
131
+ return {}
132
+ end
133
+
134
+ vars = {}
135
+ query = query.split('&').each do |var|
136
+ var = var.split('=')
137
+ if 2 == var.length
138
+ vars[var[0]] = var[1]
139
+ else
140
+ vars[var[0]] = true
141
+ end
142
+ end
143
+ vars
144
+ end
145
+
146
+ # Remove GET variables from URI
147
+ def format_url(url)
148
+ index = url.index('?')
149
+ if index.nil?
150
+ url
151
+ else
152
+ url[0...index]
153
+ end
154
+ end
155
+
156
+ # Format cookies as array
157
+ def format_cookie(string)
158
+ if string.nil? or string.empty?
159
+ return {}
160
+ end
161
+
162
+ cookies = {}
163
+ string = string.split(';')
164
+ string.each do |item|
165
+ index = item.index('=')
166
+ cookies[item[0...index].strip] = item[index+1..-1].strip
167
+ end
168
+ cookies
169
+ end
170
+
171
+ # Format POST variables
172
+ def format_post(input)
173
+ if 'application/x-www-form-urlencoded' != @env['CONTENT_TYPE']
174
+ return {}
175
+ end
176
+
177
+ post = input.read
178
+ if post.nil?
179
+ return {}
180
+ end
181
+
182
+ vars = {}
183
+ post.split('&').each do |var|
184
+ var = var.split '='
185
+ if var[1].nil?
186
+ var[1] = ''
187
+ end
188
+ vars[var[0]] = CGI.unescape(var[1].nil? ? '' : var[1])
189
+ end
190
+
191
+ vars
192
+ end
193
+
194
+ # Send HTTP headers
195
+ def send_headers
196
+ @headers.each do |header|
197
+ @out.print header.join(': ') + "\r\n"
198
+ end
199
+ @out.print "Content-type: #{@content}\r\n"
200
+ @out.print "Status: #{@status}\r\n"
201
+ @out.print "\r\n"
202
+ @header_sended = true
203
+ end
204
+
205
+ # Security check and normalizing URL
206
+ def normalize_url(url)
207
+ if '/' != url[-1..-1]
208
+ url += '/'
209
+ end
210
+ url.sub(/\/\.?\.\//, '/')
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,93 @@
1
+ =begin
2
+ Initialize system constants, load modules and start Microengine.
3
+ Copyright (C) 2008 Andrey "A.I." Sitnik <andrey@sitnik.ru>
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require 'logger'
20
+ require 'yaml'
21
+ require 'dispatcher'
22
+ require 'assambler'
23
+ require 'file_cache'
24
+ require 'memory_cache'
25
+
26
+ module Microengine
27
+ # Load configs, create pages cache if necessary
28
+ class Initializer
29
+
30
+ # Initialize microengine
31
+ def initialize
32
+ logger = Logger.new(STDERR)
33
+ logger.level = Logger::WARN
34
+ config = load_config
35
+
36
+ admin = Admin.new
37
+ dispatcher = Dispatcher.new
38
+ assambler = Assambler.new
39
+ if 'memory' == config['cache']
40
+ cache = MemoryCache.new
41
+ elsif 'file' == config['cache']
42
+ cache = FileCache.new
43
+ else
44
+ logger.warn "Unknown value '#{config['cache']}' for 'cache' in config/settings.yaml . Will be used file cache."
45
+ cache = MemoryCache.new
46
+ end
47
+
48
+ [admin, dispatcher, assambler].each do |object|
49
+ object.logger = logger
50
+ object.config = config
51
+ end
52
+ [admin, dispatcher].each do |object|
53
+ object.cache = cache
54
+ end
55
+ admin.shadow = load_shadow
56
+ admin.dispatcher = dispatcher
57
+ admin.assambler = assambler
58
+ dispatcher.spiders = load_seach_spiders
59
+ dispatcher.admin = admin
60
+
61
+ begin
62
+ assambler.refresh
63
+ cache.refresh
64
+ rescue Exception => e
65
+ logger.error "Cache was not created. " + e.message + "\n" + e.backtrace.join("\n")
66
+ end
67
+ dispatcher.run
68
+ rescue Exception => e
69
+ logger.error(e)
70
+ end
71
+
72
+ # Load settings
73
+ def load_config
74
+ YAML::load_file(MICROENGINE_ROOT + '/config/settings.yaml')
75
+ rescue
76
+ raise "Can't open settings file. Please ensure that config/settings.yaml exists and can be read by engine."
77
+ end
78
+
79
+ # Load admin password
80
+ def load_shadow
81
+ YAML::load_file(MICROENGINE_ROOT + '/config/shadow.yaml')
82
+ rescue
83
+ raise "Can't open shadow file. Please ensure that config/shadow.yaml exists and can be read by engine."
84
+ end
85
+
86
+ # Load search spider list
87
+ def load_seach_spiders
88
+ IO.readlines(MICROENGINE_ROOT + '/config/search spiders')
89
+ rescue
90
+ raise "Can't open spiders file. Please ensure that '/config/search spiders' exists and can be read by engine."
91
+ end
92
+ end
93
+ end