def login_twitter_init(request): """ Start twitter authentication: create OAuth request to twitter and pass url back to caller javascript """ c = {'authorize_url': False} consumer_key = str(get_config('tw_consumer_key')) consumer_secret = str(get_config('tw_consumer_secret')) page_url = request.POST['page_url'] callback_url = route_url('account_twitter_finish', request, _query=[('pyrone_url', page_url)]) twitter = Twitter(auth=OAuth('', '', consumer_key, consumer_secret), format='', api_version=None) try: oauth_resp = twitter.oauth.request_token(oauth_callback=callback_url) except TwitterHTTPError as e: log.error('Invalid "request_token" request: {0}'.format(str(e))) return HTTPNotFound() oauth_resp_data = dict(urllib.parse.parse_qsl(oauth_resp)) oauth_token = oauth_resp_data['oauth_token'] oauth_token_secret = oauth_resp_data['oauth_token_secret'] request.session['twitter_request_token'] = (oauth_token, oauth_token_secret) auth_url = 'https://twitter.com/oauth/authorize?{0}'.format(urllib.parse.urlencode({ 'oauth_token': oauth_token })) c['authorize_url'] = auth_url return c
def get_twitter_share_link_button(force_reload=False): value = cache.get_value('rendered_twitter_share_link_button') if value is None or force_reload: if get_config('social_twitter_share_link') != 'true': value = '' else: tpl = '''<a href="https://twitter.com/share" class="twitter-share-button"{twitter_via}{show_count}>Tweet</a>''' twitter_via = get_config('social_twitter_share_link_via') show_count = get_config('social_twitter_share_link_show_count') repl = { 'twitter_via': 'pyrone', 'show_count': '' } if twitter_via != '': # possible repl['twitter_via'] = ' data-via="{0}"'.format(html_escape(twitter_via)) if show_count != 'true': repl['show_count'] = ' data-count="none"' value = tpl.format(**repl) value += '''<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>''' cache.set_value('rendered_twitter_share_link_button', value) return value
def get_pages_widget_links(force_reload=False): value = cache.get_value('pages_links') if value is None or force_reload: pages_links = list() # fetch from settings, parse, fill cache raw = get_config('widget_pages_pages_spec') if raw is None: raw = '' for line in raw.split('\n'): line = line.strip() if len(line) == 0: continue # take first char - it's a delimiter delim = line[0] components = line[1:].split(delim) if len(components) != 2: continue url, title = components if not url.startswith('http://') and not url.startswith('https://'): continue link = {'url': url, 'title': title} pages_links.append(link) value = pages_links cache.set_value('pages_links', value) return value
def tag_articles(request): _ = request.translate tag = request.matchdict['tag'] page_size = int(get_config('elements_on_page')) start_page = 0 if 'start' in request.GET: try: start_page = int(request.GET['start']) except ValueError: start_page = 0 user = request.user dbsession = DBSession() q = dbsession.query(Article).join(Tag).options(joinedload('tags')).options(joinedload('user')).order_by(Article.published.desc()) if not user.has_role('editor'): q = q.filter(Article.is_draft==False) c = {} c['articles'] = q.filter(Tag.tag == tag)[(start_page * page_size):(start_page+1) * page_size + 1] c['prev_page'] = None if len(c['articles']) > page_size: c['prev_page'] = route_url('blog_tag_articles', request, tag=tag, _query=[('start', start_page+1)]) c['articles'].pop() c['next_page'] = None if start_page > 0: c['next_page'] = route_url('blog_tag_articles', request, tag=tag, _query=[('start', start_page-1)]) c['page_title'] = _(u'Articles labeled with tag “{0}”'.format(tag)) return c
def get_current_theme_css(): ui_theme = get_config('ui_theme', force=True) css_url = '/static/styles/{0}/blog.css' if ui_theme.endswith('.css'): css_url = '/files/f/{0}' return css_url.format(ui_theme)
def timestamp_to_dt(ts): """ Convert UTC seconds to datetime object """ tz = get_config('timezone') tts = datetime.datetime.utcfromtimestamp(ts) # seconds -> time_struct utc_dt = pytz.utc.localize(tts).astimezone(tz) # utc time -> local time return utc_dt
def get_facebook_share_button(url): if get_config('social_facebook_share') != 'true': value = '' else: tpl = '''<div class="fb-share-button" data-href="{url}" data-layout="button_count"></div>''' value = tpl.format(url=url) return value
def send(self): if not enable_email: log.debug('mail sending is not allowed in config') return sender = get_config('notifications_from_email') Process(target=send_email_process, args=(self.request, self.template_name, [self.to], sender, self.params)).start()
def gen_new_comment_admin_notification(request, article, comment): """ Generate new comment notification for the administrator. """ email = normalize_email(get_config('admin_notifications_email')) if not email: return None # placeholders replacements params = { 'site_title': get_config('site_title') } params.update(_extract_comment_sub(request, comment)) params.update(_extract_article_sub(request, comment.article)) n = Notification('admin_new_comment', email, params, request=request) return n
def str_to_timestamp(t_str): """ Convert time string in local timezone to UTC seconds """ tz = get_config('timezone') dt = datetime.datetime.strptime(t_str, '%Y-%m-%d %H:%M') dt_loc = tz.localize(dt) dt_utc = dt_loc.astimezone(pytz.utc) return calendar.timegm(dt_utc.timetuple())
def timestamp_to_str(ts, fmt='%Y-%m-%d %H:%M'): """ Convert UTC seconds to time string in local timezone """ tz = get_config('timezone') tts = datetime.datetime.utcfromtimestamp(ts) # seconds -> time_struct utc_dt = pytz.utc.localize(tts).astimezone(tz) # utc time -> local time t_str = utc_dt.strftime(fmt) return t_str
def login_twitter_finish(request): """ Finish twitter authentication """ consumer_key = str(get_config('tw_consumer_key')) consumer_secret = str(get_config('tw_consumer_secret')) token = request.session.get('twitter_request_token') twitter = Twitter(auth=OAuth(token[0], token[1], consumer_key, consumer_secret), format='', api_version=None) verifier = request.GET.get('oauth_verifier') try: oauth_resp = twitter.oauth.access_token(oauth_verifier=verifier) except TwitterHTTPError as e: log.error('Invalid "access_token" request: {0}'.format(str(e))) return HTTPNotFound() oauth_resp_data = dict(urllib.parse.parse_qsl(oauth_resp)) # typical response: # {'user_id': '128607225', 'oauth_token_secret': 'NaGQrWyNRtHHHbvm3tNI0tcr2KTBUEY0J3ng8d7KFXg', 'screen_name': 'otmenych', 'oauth_token': '128607225-NWzT8YL1Wt6qNzMLzmaCEWOxqFtrEI1pjlA8c5FK'} tw_username = oauth_resp_data['screen_name'] user = find_twitter_user(tw_username) if user is None: dbsession = DBSession() # create user user = User() user.kind = 'twitter' user.login = tw_username dbsession.add(user) # re-request again to correctly read roles user = find_twitter_user(tw_username) if user is None: log.error('Unable to create twitter user') return HTTPServerError() # save user to the session user.detach() remember(request, None, user=user) return HTTPFound(location=request.GET['pyrone_url'])
def latest_rss(request): """ Create rss feed with the latest published articles and return them as the atom feed """ _ = request.translate dbsession = DBSession() q = dbsession.query(Article).options(joinedload('tags'))\ .options(joinedload('user'))\ .filter(Article.is_draft==False).order_by(Article.updated.desc()) articles = q[0:10] rss_title = get_config('site_title') + ' - ' + _('Latest articles feed') site_base_url = get_config('site_base_url') items = [] ''' feed = Rss201rev2Feed( title=rss_title, link=site_base_url, description='', language='en') ''' for a in articles: link = h.article_url(request, a) tags_list = [] for t in a.tags: tags_list.append(t.tag) items.append(RSSItem(title=a.title, link=link, description=a.rendered_preview, pubDate=h.timestamp_to_dt(a.published), guid=str(a.id))) feed = RSS2( title=rss_title, link=site_base_url, description='', items=items ) response = Response(body=feed.to_xml(encoding='utf-8'), content_type='application/rss+xml') return response
def gen_email_verification_notification(request, email, verification_code): """ Generate email address verification notification. """ email = normalize_email(email) if not email: return None # placeholders replacements base_url = get_config('site_base_url') q = urllib.parse.urlencode({'token': verification_code, 'email': email}) verify_url = base_url + '/verify-email?' + q params = { 'site_title': get_config('site_title'), 'email': email, 'verify_url': verify_url } n = Notification('email_verification', email, params, request=request) return n
def get_gplusone_button(force_reload=False): value = cache.get_value('rendered_gplusone_button') if value is None or force_reload: if get_config('social_gplusone') != 'true': value = '' else: tpl = '''<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <g:plusone></g:plusone>''' value = tpl cache.set_value('rendered_gplusone_button', value) return value
def get_facebook_share_button_script(): if get_config('social_facebook_share') != 'true': value = '' else: tpl = '''<!-- facebook share button --> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script> <!-- facebook share button -->''' value = tpl return value
def latest(request): """ Display list of articles sorted by publishing date ascending, show rendered previews, not complete articles """ _ = request.translate c = {'articles': []} headers = [] for k,v in request.headers.items(): headers.append('{0}: {1}'.format(k, v)) page_size = int(get_config('elements_on_page')) start_page = 0 if 'start' in request.GET: try: start_page = int(request.GET['start']) except ValueError: start_page = 0 dbsession = DBSession() user = request.user q = dbsession.query(Article).options(joinedload('tags')).options(joinedload('user')).order_by(Article.published.desc()) if not user.has_role('editor'): q = q.filter(Article.is_draft==False) c['articles'] = q[(start_page * page_size):(start_page+1) * page_size + 1] #for article in c['articles']: # log.debug(article.shortcut_date) c['prev_page'] = None if len(c['articles']) > page_size: c['prev_page'] = route_url('blog_latest', request, _query=[('start', start_page+1)]) c['articles'].pop() c['next_page'] = None if start_page > 0: c['next_page'] = route_url('blog_latest', request, _query=[('start', start_page-1)]) c['page_title'] = _('Latest articles') return c
def gen_comment_response_notification(request, article, comment, top_comment, email): """ Generate comment answer notification """ email = normalize_email(email) if not email: return None # placeholders replacements params = { 'site_title': get_config('site_title') } params.update(_extract_comment_sub(request, comment)) params.update(_extract_article_sub(request, article)) n = Notification('comment_response', email, params, request=request) return n
def download_file_preview(request): filename = request.matchdict['filename'] dbsession = DBSession() file = dbsession.query(File).filter(File.name == filename).first() if file is None: return HTTPNotFound() if file.content_type not in ('image/jpeg', 'image/png', 'image/jpg'): return HTTPNotFound() headers = [] dltype = file.dltype if 'dltype' in request.GET and request.GET['dltype'] in allowed_dltypes: dltype = request.GET['dltype'] if dltype == 'download': headers.append(('Content-Disposition', str('attachment; filename=preview_{0}'.format(file.name)))) storage_dirs = get_storage_dirs() full_path = os.path.join(storage_dirs['orig'], filename) preview_path = os.path.join(storage_dirs['img_preview_mid'], filename) preview_path += '.png' # always save preview in PNG format if os.path.exists(preview_path) and not os.path.isfile(preview_path): log.error('Path to preview image "{0}" must be a regular file!'.format(preview_path)) return HTTPServerError() # make preview image if required if not os.path.exists(preview_path): try: preview_max_width = int(get_config('image_preview_width', 300)) except ValueError: preview_max_width = 300 except TypeError: preview_max_width = 300 im = Image.open(full_path) if im.size[0] <= preview_max_width: # don't need a resize preview_path = full_path else: h = (im.size[1] * preview_max_width) / im.size[0] resized = im.resize((preview_max_width, int(h)), Image.BILINEAR) resized.save(preview_path) try: content_length = os.path.getsize(preview_path) headers += [('Content-Length', str(content_length))] except IOError: return HTTPNotFound() response = Response(content_type=str(file.content_type)) try: response.app_iter = open(preview_path, 'rb') except IOError: return HTTPNotFound() response.headerlist += headers return response
def add_article_comment_ajax(request): _ = request.translate article_id = int(request.matchdict['article_id']) dbsession = DBSession() q = dbsession.query(Article).filter(Article.id == article_id) user = request.user if not user.has_role('editor') or not user.has_role('admin'): q = q.filter(Article.is_draft==False) article = q.first() if article is None or not article.is_commentable: return HTTPNotFound() if 's' not in request.POST: return HTTPBadRequest() json = {} key = request.POST['s'] # all data elements are constructed from the string "key" as substrings body_ind = key[3:14] parent_ind = key[4:12] display_name_ind = key[0:5] email_ind = key[13:25] website_ind = key[15:21] is_subscribed_ind = key[19:27] for ind in (body_ind, parent_ind, display_name_ind, email_ind, website_ind): if ind not in request.POST: return HTTPBadRequest() body = request.POST[body_ind] if len(body) == 0: return {'error': _('Empty comment body is not allowed.')} comment = Comment() comment.set_body(body) user = request.user if user.kind != 'anonymous': comment.user_id = user.id else: # get "email", "display_name" and "website" arguments comment.display_name = request.POST[display_name_ind] comment.email = request.POST[email_ind] comment.website = request.POST[website_ind] # remember email, display_name and website in browser cookies request.response.set_cookie('comment_display_name', comment.display_name, max_age=31536000) request.response.set_cookie('comment_email', comment.email, max_age=31536000) request.response.set_cookie('comment_website', comment.website, max_age=31536000) # set parent comment parent_id = request.POST[parent_ind] try: parent_id = int(parent_id) except ValueError: parent_id = None if parent_id: parent = dbsession.query(Comment).filter(Comment.id == parent_id)\ .filter(Comment.article_id == article_id).first() if parent is not None: if not parent.is_approved: # data = { 'error': _('Answering to not approved comment')} return json.dumps(data) comment.parent_id = parent_id comment.article_id = article_id if is_subscribed_ind in request.POST: comment.is_subscribed = True # this list contains notifications ns = [] # if user has subscribed to answer then check is his/her email verified # if doesn't send verification message to the email if is_subscribed_ind in request.POST: vrf_email = '' if user.kind != 'anonymous': vrf_email = user.email elif request.POST[email_ind]: vrf_email = request.POST[email_ind] vrf_email = normalize_email(vrf_email) if vrf_email: # email looks ok so proceed send_evn = False vf = dbsession.query(VerifiedEmail).filter(VerifiedEmail.email == vrf_email).first() vf_token = '' if vf is not None: if not vf.is_verified: diff = time() - vf.last_verify_date #if diff > 86400: if diff > 1: # delay between verifications requests must be more than 24 hours send_evn = True vf.last_verify_date = time() vf_token = vf.verification_code else: send_evn = True vf = VerifiedEmail(vrf_email) vf_token = vf.verification_code dbsession.add(vf) if send_evn: ns.append(notifications.gen_email_verification_notification(request, vrf_email, vf_token)) request.response.set_cookie('is_subscribed', 'true' if comment.is_subscribed else 'false', max_age=31536000) # automatically approve comment if user has role "admin", "writer" or "editor" if user.has_role('admin') or user.has_role('writer') \ or user.has_role('editor'): comment.is_approved = True # TODO: also automatically approve comment if it's considered as safe: # i.e. without hyperlinks, spam etc # check how much hyperlinks in the body string if len(re.findall('https?://', body, flags=re.IGNORECASE)) <= 1: comment.is_approved = True # record commenter ip address comment.ip_address = request.environ.get('REMOTE_ADDR', 'unknown') comment.xff_ip_address = request.environ.get('X_FORWARDED_FOR', None) dbsession.add(comment) _update_comments_counters(dbsession, article) dbsession.flush() dbsession.expunge(comment) # remove object from the session, object state is preserved dbsession.expunge(article) transaction.commit() # to delete, probably # comment added, now send notifications loop_limit = 100 comment = dbsession.query(Comment).get(comment.id) parent = comment.parent admin_email = get_config('admin_notifications_email') vf_q = dbsession.query(VerifiedEmail) notifications_emails = [] while parent is not None and loop_limit > 0: loop_limit -= 1 c = parent parent = c.parent # walk up the tree if not c.is_subscribed: continue # find email email = None if c.user is None: email = c.email else: email = c.user.email if email is None or email == admin_email: continue email = normalize_email(email) if email in notifications_emails: continue vf = vf_q.filter(VerifiedEmail.email == email).first() if vf is not None and vf.is_verified: # send notification to "email" ns.append(notifications.gen_comment_response_notification(request, article, comment, c, email)) admin_notifications_email = normalize_email(get_config('admin_notifications_email')) for nfn in ns: if nfn is None: continue if nfn.to == admin_notifications_email: continue nfn.send() # create special notification for the administrator nfn = notifications.gen_new_comment_admin_notification(request, article, comment) if nfn is not None: nfn.send() # cosntruct comment_id # we're not using route_url() for the article because stupid Pyramid urlencodes fragments comment_url = h.article_url(request, article) + '?commentid=' + str(comment.id) # return rendered comment data = { 'body': comment.rendered_body, 'approved': comment.is_approved, 'id': comment.id, 'url': comment_url } return data