Added some locale determination tools
[mediagoblin.git] / mediagoblin / util.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 Free Software Foundation, Inc
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 from email.MIMEText import MIMEText
18 import smtplib
19 import sys
20
21 import jinja2
22 import mongokit
23
24 from mediagoblin import globals as mgoblin_globals
25
26
27 TESTS_ENABLED = False
28 def _activate_testing():
29 """
30 Call this to activate testing in util.py
31 """
32 global TESTS_ENABLED
33 TESTS_ENABLED = True
34
35
36 def get_jinja_env(user_template_path=None):
37 """
38 Set up the Jinja environment, possibly allowing for user
39 overridden templates.
40
41 (In the future we may have another system for providing theming;
42 for now this is good enough.)
43 """
44 if user_template_path:
45 loader = jinja2.ChoiceLoader(
46 [jinja2.FileSystemLoader(user_template_path),
47 jinja2.PackageLoader('mediagoblin', 'templates')])
48 else:
49 loader = jinja2.PackageLoader('mediagoblin', 'templates')
50
51 return jinja2.Environment(
52 loader=loader, autoescape=True,
53 extensions=['jinja2.ext.i18n'])
54
55
56 def setup_user_in_request(request):
57 """
58 Examine a request and tack on a request.user parameter if that's
59 appropriate.
60 """
61 if not request.session.has_key('user_id'):
62 request.user = None
63 return
64
65 user = None
66 user = request.app.db.User.one(
67 {'_id': mongokit.ObjectId(request.session['user_id'])})
68
69 if not user:
70 # Something's wrong... this user doesn't exist? Invalidate
71 # this session.
72 request.session.invalidate()
73
74 request.user = user
75
76
77 def import_component(import_string):
78 """
79 Import a module component defined by STRING. Probably a method,
80 class, or global variable.
81
82 Args:
83 - import_string: a string that defines what to import. Written
84 in the format of "module1.module2:component"
85 """
86 module_name, func_name = import_string.split(':', 1)
87 __import__(module_name)
88 module = sys.modules[module_name]
89 func = getattr(module, func_name)
90 return func
91
92
93 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
94 ### Special email test stuff begins HERE
95 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96
97 # We have two "test inboxes" here:
98 #
99 # EMAIL_TEST_INBOX:
100 # ----------------
101 # If you're writing test views, you'll probably want to check this.
102 # It contains a list of MIMEText messages.
103 #
104 # EMAIL_TEST_MBOX_INBOX:
105 # ----------------------
106 # This collects the messages from the FakeMhost inbox. It's reslly
107 # just here for testing the send_email method itself.
108 #
109 # Anyway this contains:
110 # - from
111 # - to: a list of email recipient addresses
112 # - message: not just the body, but the whole message, including
113 # headers, etc.
114 #
115 # ***IMPORTANT!***
116 # ----------------
117 # Before running tests that call functions which send email, you should
118 # always call _clear_test_inboxes() to "wipe" the inboxes clean.
119
120 EMAIL_TEST_INBOX = []
121 EMAIL_TEST_MBOX_INBOX = []
122
123
124 class FakeMhost(object):
125 """
126 Just a fake mail host so we can capture and test messages
127 from send_email
128 """
129 def connect(self):
130 pass
131
132 def sendmail(self, from_addr, to_addrs, message):
133 EMAIL_TEST_MBOX_INBOX.append(
134 {'from': from_addr,
135 'to': to_addrs,
136 'message': message})
137
138 def _clear_test_inboxes():
139 global EMAIL_TEST_INBOX
140 global EMAIL_TEST_MBOX_INBOX
141 EMAIL_TEST_INBOX = []
142 EMAIL_TEST_MBOX_INBOX = []
143
144 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
145 ### </Special email test stuff>
146 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
147
148 def send_email(from_addr, to_addrs, subject, message_body):
149 """
150 Simple email sending wrapper, use this so we can capture messages
151 for unit testing purposes.
152
153 Args:
154 - from_addr: address you're sending the email from
155 - to_addrs: list of recipient email addresses
156 - subject: subject of the email
157 - message_body: email body text
158 """
159 # TODO: make a mock mhost if testing is enabled
160 if TESTS_ENABLED or mgoblin_globals.email_debug_mode:
161 mhost = FakeMhost()
162 elif not mgoblin_globals.email_debug_mode:
163 mhost = smtplib.SMTP()
164
165 mhost.connect()
166
167 message = MIMEText(message_body.encode('utf-8'), 'plain', 'utf-8')
168 message['Subject'] = subject
169 message['From'] = from_addr
170 message['To'] = ', '.join(to_addrs)
171
172 if TESTS_ENABLED:
173 EMAIL_TEST_INBOX.append(message)
174
175 elif mgoblin_globals.email_debug_mode:
176 print u"===== Email ====="
177 print u"From address: %s" % message['From']
178 print u"To addresses: %s" % message['To']
179 print u"Subject: %s" % message['Subject']
180 print u"-- Body: --"
181 print message.get_payload(decode=True)
182
183 else:
184 return mhost.sendmail(from_addr, to_addrs, message.as_string())
185
186
187 ###################
188 # Translation tools
189 ###################
190
191
192 def locale_to_lower_upper(locale):
193 """
194 Take a locale, regardless of style, and format it like "en-us"
195 """
196 if '-' in locale:
197 lang, country = locale.split('-', 1)
198 return '%s_%s' % (lang.lower(), country.upper())
199 elif '_' in locale:
200 lang, country = locale.split('_', 1)
201 return '%s_%s' % (lang.lower(), country.upper())
202 else:
203 return locale.lower()
204
205
206 def locale_to_lower_lower(locale):
207 """
208 Take a locale, regardless of style, and format it like "en_US"
209 """
210 if '_' in locale:
211 lang, country = locale.split('_', 1)
212 return '%s-%s' % (lang.lower(), country.lower())
213 else:
214 return locale.lower()
215
216
217 def get_locale_from_request(request):
218 """
219 Figure out what target language is most appropriate based on the
220 request
221 """
222 request_form = request.GET or request.POST
223
224 if request_form.has_key('lang'):
225 return locale_to_lower_upper(request_form['lang'])
226
227 accept_lang_matches = request.accept_language.best_matches()
228
229 # Your routing can explicitly specify a target language
230 if request.matchdict.has_key('target_lang'):
231 target_lang = request.matchdict['target_lang']
232 elif request.session.has_key('target_lang'):
233 target_lang = request.session['target_lang']
234 # Pull the first acceptable language
235 elif accept_lang_matches:
236 target_lang = accept_lang_matches[0]
237 # Fall back to English
238 else:
239 target_lang = 'en'
240
241 return make_locale_lower_upper_style(target_lang)