示例#1
0
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
示例#2
0
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
示例#3
0
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
示例#4
0
文件: blog.py 项目: sigsergv/pyrone
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
示例#5
0
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)
示例#6
0
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
示例#7
0
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
示例#8
0
    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()
示例#9
0
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
示例#10
0
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())
示例#11
0
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
示例#12
0
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'])
示例#13
0
文件: blog.py 项目: sigsergv/pyrone
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
示例#14
0
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
示例#15
0
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
示例#16
0
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
示例#17
0
文件: blog.py 项目: sigsergv/pyrone
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
示例#18
0
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
示例#19
0
文件: blog.py 项目: sigsergv/pyrone
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
示例#20
0
文件: blog.py 项目: sigsergv/pyrone
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