867c4380b9a4852468106477d21286f4984eb71c
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 Free Software Foundation, Inc
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.
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.
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/>.
17 from email
.MIMEText
import MIMEText
24 from mediagoblin
.db
.util
import ObjectId
27 from mediagoblin
import globals as mgoblin_globals
36 def _activate_testing():
38 Call this to activate testing in util.py
44 def get_jinja_loader(user_template_path
=None):
46 Set up the Jinja template loaders, possibly allowing for user
49 (In the future we may have another system for providing theming;
50 for now this is good enough.)
52 if user_template_path
:
53 return jinja2
.ChoiceLoader(
54 [jinja2
.FileSystemLoader(user_template_path
),
55 jinja2
.PackageLoader('mediagoblin', 'templates')])
57 return jinja2
.PackageLoader('mediagoblin', 'templates')
60 def get_jinja_env(template_loader
, locale
):
62 Set up the Jinja environment,
64 (In the future we may have another system for providing theming;
65 for now this is good enough.)
69 template_env
= jinja2
.Environment(
70 loader
=template_loader
, autoescape
=True,
71 extensions
=['jinja2.ext.i18n'])
73 template_env
.install_gettext_callables(
74 mgoblin_globals
.translations
.gettext
,
75 mgoblin_globals
.translations
.ngettext
)
80 def setup_user_in_request(request
):
82 Examine a request and tack on a request.user parameter if that's
85 if not request
.session
.has_key('user_id'):
90 user
= request
.app
.db
.User
.one(
91 {'_id': ObjectId(request
.session
['user_id'])})
94 # Something's wrong... this user doesn't exist? Invalidate
96 request
.session
.invalidate()
101 def import_component(import_string
):
103 Import a module component defined by STRING. Probably a method,
104 class, or global variable.
107 - import_string: a string that defines what to import. Written
108 in the format of "module1.module2:component"
110 module_name
, func_name
= import_string
.split(':', 1)
111 __import__(module_name
)
112 module
= sys
.modules
[module_name
]
113 func
= getattr(module
, func_name
)
116 _punct_re
= re
.compile(r
'[\t !"#$%&\'()*\
-/<=>?
@\
[\\\
]^_`
{|
},.]+')
118 def slugify(text, delim=u'-'):
120 Generates an ASCII-only slug. Taken from http://flask.pocoo.org/snippets/5/
123 for word in _punct_re.split(text.lower()):
124 word = word.encode('translit
/long')
127 return unicode(delim.join(result))
129 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130 ### Special email test stuff begins HERE
131 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
133 # We have two "test inboxes" here:
137 # If you're writing test views
, you
'll probably want to check this.
138 # It contains a list of MIMEText messages.
140 # EMAIL_TEST_MBOX_INBOX:
141 # ----------------------
142 # This collects the messages from the FakeMhost inbox. It's reslly
143 # just here for testing the send_email method itself.
145 # Anyway this contains:
147 # - to: a list of email recipient addresses
148 # - message: not just the body, but the whole message, including
153 # Before running tests that call functions which send email, you should
154 # always call _clear_test_inboxes() to "wipe" the inboxes clean.
156 EMAIL_TEST_INBOX
= []
157 EMAIL_TEST_MBOX_INBOX
= []
160 class FakeMhost(object):
162 Just a fake mail host so we can capture and test messages
168 def sendmail(self
, from_addr
, to_addrs
, message
):
169 EMAIL_TEST_MBOX_INBOX
.append(
174 def _clear_test_inboxes():
175 global EMAIL_TEST_INBOX
176 global EMAIL_TEST_MBOX_INBOX
177 EMAIL_TEST_INBOX
= []
178 EMAIL_TEST_MBOX_INBOX
= []
180 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
181 ### </Special email test stuff>
182 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
184 def send_email(from_addr
, to_addrs
, subject
, message_body
):
186 Simple email sending wrapper, use this so we can capture messages
187 for unit testing purposes.
190 - from_addr: address you're sending the email from
191 - to_addrs: list of recipient email addresses
192 - subject: subject of the email
193 - message_body: email body text
195 # TODO: make a mock mhost if testing is enabled
196 if TESTS_ENABLED
or mgoblin_globals
.email_debug_mode
:
198 elif not mgoblin_globals
.email_debug_mode
:
199 mhost
= smtplib
.SMTP()
203 message
= MIMEText(message_body
.encode('utf-8'), 'plain', 'utf-8')
204 message
['Subject'] = subject
205 message
['From'] = from_addr
206 message
['To'] = ', '.join(to_addrs
)
209 EMAIL_TEST_INBOX
.append(message
)
211 if getattr(mgoblin_globals
, 'email_debug_mode', False):
212 print u
"===== Email ====="
213 print u
"From address: %s" % message
['From']
214 print u
"To addresses: %s" % message
['To']
215 print u
"Subject: %s" % message
['Subject']
217 print message
.get_payload(decode
=True)
219 return mhost
.sendmail(from_addr
, to_addrs
, message
.as_string())
227 TRANSLATIONS_PATH
= pkg_resources
.resource_filename(
228 'mediagoblin', 'translations')
231 def locale_to_lower_upper(locale
):
233 Take a locale, regardless of style, and format it like "en-us"
236 lang
, country
= locale
.split('-', 1)
237 return '%s_%s' % (lang
.lower(), country
.upper())
239 lang
, country
= locale
.split('_', 1)
240 return '%s_%s' % (lang
.lower(), country
.upper())
242 return locale
.lower()
245 def locale_to_lower_lower(locale
):
247 Take a locale, regardless of style, and format it like "en_US"
250 lang
, country
= locale
.split('_', 1)
251 return '%s-%s' % (lang
.lower(), country
.lower())
253 return locale
.lower()
256 def get_locale_from_request(request
):
258 Figure out what target language is most appropriate based on the
261 request_form
= request
.GET
or request
.POST
263 if request_form
.has_key('lang'):
264 return locale_to_lower_upper(request_form
['lang'])
266 accept_lang_matches
= request
.accept_language
.best_matches()
268 # Your routing can explicitly specify a target language
269 if request
.matchdict
.has_key('locale'):
270 target_lang
= request
.matchdict
['locale']
271 elif request
.session
.has_key('target_lang'):
272 target_lang
= request
.session
['target_lang']
273 # Pull the first acceptable language
274 elif accept_lang_matches
:
275 target_lang
= accept_lang_matches
[0]
276 # Fall back to English
280 return locale_to_lower_upper(target_lang
)
283 def setup_gettext(locale
):
285 Setup the gettext instance based on this locale
287 # Later on when we have plugins we may want to enable the
288 # multi-translations system they have so we can handle plugin
291 # TODO: fallback nicely on translations from pt_PT to pt if not
293 this_gettext
= gettext
.translation(
294 'mediagoblin', TRANSLATIONS_PATH
, [locale
], fallback
=True)
296 mgoblin_globals
.setup_globals(
297 translations
=this_gettext
)
300 class Pagination(object):
303 initialization through __init__(self, cursor, page=1, per_page=2):
304 get actual data slice through __call__()
307 def __init__(self
, page
, cursor
, per_page
=2):
309 Initializes Pagination
312 - page: requested page
313 - per_page: number of objects per page
317 self
.per_page
= per_page
319 self
.total_count
= self
.cursor
.count()
323 Returns slice of objects for the requested page
325 return self
.cursor
.skip(
326 (self
.page
- 1) * self
.per_page
).limit(self
.per_page
)
330 return int(ceil(self
.total_count
/ float(self
.per_page
)))
338 return self
.page
< self
.pages
340 def iter_pages(self
, left_edge
=2, left_current
=2,
341 right_current
=5, right_edge
=2):
343 for num
in xrange(1, self
.pages
+ 1):
344 if num
<= left_edge
or \
345 (num
> self
.page
- left_current
- 1 and \
346 num
< self
.page
+ right_current
) or \
347 num
> self
.pages
- right_edge
:
353 def get_page_url(self
, path_info
, page_no
, get_params
=None):
355 Get a new page based of the path_info, the new page number,
356 and existing get parameters.
358 new_get_params
= copy
.copy(get_params
or {})
359 new_get_params
['page'] = page_no
361 path_info
, urllib
.urlencode(new_get_params
))