зеркало из https://github.com/mozilla/kitsune.git
Customer Care. Twitter OAuth and template fixes.
This commit is contained in:
Родитель
cc2c1143c5
Коммит
1ad3f3567f
|
@ -8,7 +8,7 @@ from sumo.models import ModelBase
|
|||
|
||||
class Tweet(ModelBase):
|
||||
"""An entry on twitter."""
|
||||
tweet_id = models.BigIntegerField()
|
||||
tweet_id = models.BigIntegerField(unique=True)
|
||||
raw_json = models.TextField()
|
||||
locale = models.CharField(max_length=20, db_index=True)
|
||||
created = models.DateTimeField(default=datetime.now, db_index=True)
|
||||
|
|
|
@ -5,46 +5,41 @@
|
|||
{% block breadcrumbs %}{% endblock %}
|
||||
|
||||
{% block content_area %}
|
||||
<div class="feature-contents">
|
||||
<h2>Join our <br />Army of Awesome</h2>
|
||||
<h3>Love Firefox and have a few moments to help? Help other Firefox users on Twitter. Good things will come to those who tweet!</h3>
|
||||
</div>
|
||||
<div class="feature-contents">
|
||||
<h2>Join our <br />Army of Awesome</h2>
|
||||
<h3>Love Firefox and have a few moments to help? Help other Firefox users on Twitter. Good things will come to those who tweet!</h3>
|
||||
</div>
|
||||
|
||||
<div id="speach-bubbles">
|
||||
<ol>
|
||||
<li class="choose">Choose a tweet below</li>
|
||||
<li class="signin">Sign in with Twitter</li>
|
||||
<li class="respond">Respond to the tweet!</li>
|
||||
</ol>
|
||||
<br style="clear:both; height: 1px" />
|
||||
</div>
|
||||
<div id="speach-bubbles">
|
||||
<ol>
|
||||
<li class="choose">Choose a tweet below</li>
|
||||
<li class="signin">Sign in with Twitter</li>
|
||||
<li class="respond">Respond to the tweet!</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="tweetcontainer">
|
||||
<div class="tweets-header">
|
||||
<img src="{{ MEDIA_URL }}/img/customercare/twitter-icon.png" /><h2 class="showhide_heading" id="Where_to_ask_your_question">Choose a tweet to help</h2>
|
||||
{% if authed %}
|
||||
<a href="?twitter_delete_auth=1" id="twitter-logout">Log out of Twitter</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<br style="clear:both; height: 1px" />
|
||||
|
||||
<ul id="tweets">
|
||||
{% for tweet in tweets %}
|
||||
<li class="tweet">
|
||||
<li class="tweet" data-reply_to="{{ tweet.reply_to }}">
|
||||
<a href="http://twitter.com/{{ tweet.user }}" class="avatar"><img src="{{ tweet.profile_img }}" /></a>
|
||||
<span><span class="twittername">{{ tweet.user }}</span><span class="time">{{ tweet.date|utctimesince }}</span>
|
||||
<span class="text">{{ tweet.text }}</span>
|
||||
<span class="twittername">{{ tweet.user }}</span><span class="time">{{ tweet.date|utctimesince }}</span>
|
||||
<p class="text">{{ tweet.text }}</p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="reply-modal">
|
||||
{% include 'customercare/reply_modal.html' %}
|
||||
</div>
|
||||
{% include 'customercare/reply_modal.html' %}
|
||||
|
||||
<div id="twitter-modal">
|
||||
<h2>Sign in with your Twitter account</h2>
|
||||
<p>Before you join the Army of Awesome, you need to log in so you can respond to tweets. You will now be redirected to Twitter to log in.</p>
|
||||
<a href="{{ url('customercare.twitter_auth') }}">Sign in</a>
|
||||
<a href="#" class="cancel">Cancel</a>
|
||||
</div>
|
||||
{% include 'customercare/twitter_modal.html' %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,47 +1,51 @@
|
|||
<div id="reply-container">
|
||||
<div id="initial-tweet">
|
||||
<div id="reply-modal">
|
||||
<div id="reply-container">
|
||||
|
||||
<div id="initial-tweet">
|
||||
<a href="" class="avatar"><img src="" /></a>
|
||||
<span class="box">
|
||||
<img src="{{ MEDIA_URL }}img/customercare/initial-tweet-arrow.png" alt="" id="arrow" />
|
||||
<a href="" class="twittername"></a>
|
||||
<span class="text"></span>
|
||||
<img src="{{ MEDIA_URL }}img/customercare/initial-tweet-arrow.png" alt="" id="arrow" />
|
||||
<a href="" class="twittername"></a>
|
||||
<span class="text"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="replies">
|
||||
<h4>What is your reply about?</h4>
|
||||
<div id="accordion">
|
||||
{% for resp in canned_responses %}
|
||||
<h3><a href="#">{{ resp.title }}</a></h3>
|
||||
<div>
|
||||
<ul class="topics">
|
||||
{% for topic in resp.responses.all() %}
|
||||
<li>
|
||||
<a class="reply-topic" href="#">{{ topic.title }}</a>
|
||||
<span class="snippet">{{ topic.response }} #fxhelp</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hrbreak"></div>
|
||||
<div id="replies">
|
||||
<h4>What is your reply about?</h4>
|
||||
<div id="accordion">
|
||||
{% for resp in canned_responses %}
|
||||
<h3><a href="#">{{ resp.title }}</a></h3>
|
||||
<div>
|
||||
<ul class="topics">
|
||||
{% for topic in resp.responses.all() %}
|
||||
<li>
|
||||
<a class="reply-topic" href="#">{{ topic.title }}</a>
|
||||
<span class="snippet">{{ topic.response }} #fxhelp</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div id="reply">
|
||||
<h4>Get personal</h4>
|
||||
<div class="hrbreak"></div>
|
||||
|
||||
<div id="reply">
|
||||
<h4>Get personal</h4>
|
||||
<div class="container">
|
||||
<div class="character-counter">140</div>
|
||||
<form action="{{ url('customercare.twitter_post') }}" method="POST">
|
||||
<div class="inner-container">
|
||||
<img src="{{ MEDIA_URL }}img/customercare/reply-arrow.png" alt="" id="reply-arrow" />
|
||||
<textarea class="reply-message" placeholder="Tweak it and make it your own. Personalized messages go a long way in helping others."></textarea>
|
||||
</div>
|
||||
|
||||
<span class="submit-message">Your message was sent!</span>
|
||||
<input type="submit" value="Submit" name="" id="submit" class="submitButton" title="Submit">
|
||||
</div>
|
||||
<span id="submit-message">Your message was sent!</span>
|
||||
<input type="hidden" name="reply_to" id="reply_to">
|
||||
<input type="submit" value="Submit" name="submit" id="submit" class="submitButton" title="Submit">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<div id="twitter-modal" data-authed="{{ authed }}">
|
||||
<h2>Sign in with your Twitter account</h2>
|
||||
<p>Before you join the Army of Awesome, you need to log in so you can respond to tweets. You will now be redirected to Twitter to log in.</p>
|
||||
<a href="#" class="cancel">Cancel</a>
|
||||
<a href="?twitter_auth_request=1" class="signin">Sign in</a>
|
||||
</div>
|
|
@ -1,6 +1,6 @@
|
|||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('customercare.views',
|
||||
url(r'/twitter_auth', 'twitter_auth', name="customercare.twitter_auth"),
|
||||
url(r'/twitter_post', 'twitter_post', name="customercare.twitter_post"),
|
||||
url(r'', 'landing', name='customercare.landing'),
|
||||
)
|
||||
|
|
|
@ -2,55 +2,24 @@ from datetime import datetime
|
|||
from email.Utils import parsedate
|
||||
import json
|
||||
import logging
|
||||
from uuid import uuid4
|
||||
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
import jingo
|
||||
import tweepy
|
||||
|
||||
from .models import CannedCategory, Tweet
|
||||
import twitter
|
||||
|
||||
log = logging.getLogger('k')
|
||||
|
||||
|
||||
log = logging.getLogger('custcare')
|
||||
|
||||
token_cache_prefix = 'custcare_token_'
|
||||
key_prefix = token_cache_prefix + 'key_'
|
||||
secret_prefix = token_cache_prefix + 'secret_'
|
||||
|
||||
# cookie names are duplicated in js/cusomtercare.js
|
||||
access_cookie_name = 'custcare_twitter_access_id'
|
||||
redirect_cookie_name = 'custcare_twitter_redirect_flag'
|
||||
|
||||
|
||||
def auth_factory(request):
|
||||
return tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY,
|
||||
settings.TWITTER_CONSUMER_SECRET,
|
||||
'https://{0}/{1}/customercare/'.format(
|
||||
request.get_host(), request.locale))
|
||||
|
||||
|
||||
def set_access_cookie(resp, id):
|
||||
resp.set_cookie(redirect_cookie_name, '1', httponly=True)
|
||||
resp.set_cookie(access_cookie_name, id, secure=True)
|
||||
|
||||
|
||||
def set_tokens(id, key, secret):
|
||||
cache.set(key_prefix + id, key)
|
||||
cache.set(secret_prefix + id, secret)
|
||||
|
||||
|
||||
def get_tokens(id):
|
||||
key = cache.get(key_prefix + id)
|
||||
secret = cache.get(secret_prefix + id)
|
||||
return key, secret
|
||||
|
||||
|
||||
@twitter.auth_wanted
|
||||
def landing(request):
|
||||
"""Customer Care Landing page."""
|
||||
|
||||
twitter = request.twitter
|
||||
|
||||
canned_responses = CannedCategory.objects.all()
|
||||
tweets = []
|
||||
for tweet in Tweet.objects.filter(locale='en')[:10]:
|
||||
|
@ -61,64 +30,26 @@ def landing(request):
|
|||
'profile_img': data['profile_image_url'],
|
||||
'user': data['from_user'],
|
||||
'text': tweet,
|
||||
'reply_to': tweet.tweet_id,
|
||||
'date': date,
|
||||
})
|
||||
|
||||
resp = jingo.render(request, 'customercare/landing.html', {
|
||||
'canned_responses': canned_responses,
|
||||
'tweets': tweets,
|
||||
'now': datetime.utcnow(),
|
||||
'authed': twitter.authed,
|
||||
})
|
||||
|
||||
# TODO HTTP redirect flag checking?
|
||||
if request.COOKIES.get(redirect_cookie_name):
|
||||
return http.HttpResponseRedirect('https://{0}/{1}'.format(
|
||||
request.get_host(), request.get_full_path()))
|
||||
|
||||
# if GET[oauth_verifier] exists, we're handling an OAuth login
|
||||
verifier = request.GET.get('oauth_verifier')
|
||||
if verifier:
|
||||
auth = auth_factory(request)
|
||||
request_key = request.COOKIES.get('request_token_key')
|
||||
request_secret = request.COOKIES.get('request_token_secret')
|
||||
if request_key and request_secret:
|
||||
resp.delete_cookie('request_token_key')
|
||||
resp.delete_cookie('request_token_secret')
|
||||
auth.set_request_token(request_key, request_secret)
|
||||
|
||||
try:
|
||||
auth.get_access_token(verifier)
|
||||
except tweepy.TweepError:
|
||||
log.warning('Tweepy Error with verifier token')
|
||||
pass
|
||||
else:
|
||||
access_id = uuid4().hex
|
||||
set_access_cookie(resp, access_id)
|
||||
set_tokens(access_id, auth.access_token.key, auth.access_token.secret)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@twitter.auth_required
|
||||
def twitter_post(request):
|
||||
# access_id = request.COOKIES.get(access_cookie_name)
|
||||
# if access_id:
|
||||
# key, secret = get_tokens(access_id)
|
||||
# authed = True
|
||||
# resp.write('key: %s sec: %s' % (key, secret))
|
||||
# set_access_cookie(resp, access_id)
|
||||
pass
|
||||
|
||||
|
||||
def twitter_auth(request):
|
||||
auth = auth_factory(request)
|
||||
|
||||
try:
|
||||
redirect_url = auth.get_authorization_url()
|
||||
except tweepy.TweepError:
|
||||
log.warning('Tweepy error while getting authorization url')
|
||||
return http.HttpReponseServerError()
|
||||
|
||||
resp = http.HttpResponseRedirect(redirect_url)
|
||||
resp.set_cookie('request_token_key', auth.request_token.key, max_age=3600, secure=True)
|
||||
resp.set_cookie('request_token_secret', auth.request_token.secret, max_age=3600, secure=True)
|
||||
return resp
|
||||
# FIXME ensure post length is under twitter limit
|
||||
# do this in JS too
|
||||
tweet = request.POST.get('tweet')
|
||||
reply_to = request.POST.get('reply_to')
|
||||
# TODO remove debug line
|
||||
request.twitter.api.update_status(tweet, '25684040574')
|
||||
return http.HttpResponse()
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import logging
|
||||
from uuid import uuid4
|
||||
|
||||
from django import http
|
||||
from django.core.cache import cache
|
||||
|
||||
import tweepy
|
||||
|
||||
|
||||
log = logging.getLogger('k')
|
||||
|
||||
PREFIX = 'custcare_'
|
||||
ACCESS_NAME = PREFIX + 'access'
|
||||
REDIRECT_NAME = PREFIX + 'redirect'
|
||||
REQUEST_KEY_NAME = PREFIX + 'request_key'
|
||||
REQUEST_SECRET_NAME = PREFIX + 'request_secret'
|
||||
|
||||
MAX_AGE = 3600
|
||||
|
||||
|
||||
def ssl_url(request):
|
||||
return 'https://{0}{1}'.format(request.get_host(), request.get_full_path())
|
||||
|
||||
# Twitter sessions are SSL only, so redirect to SSL if needed
|
||||
def auth_wanted(view_func):
|
||||
def wrapper(request, *args, **kwargs):
|
||||
if request.COOKIES.get(REDIRECT_NAME) and not request.is_secure():
|
||||
return http.HttpResponseRedirect(ssl_url(request))
|
||||
return view_func(request, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
# returns a HttpResponseBadRequest in not authed
|
||||
def auth_required(view_func):
|
||||
def wrapper(request, *args, **kwargs):
|
||||
if not request.twitter.authed:
|
||||
return http.HttpResponseBadRequest()
|
||||
return view_func(request, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
class Session(object):
|
||||
id = None
|
||||
key = None
|
||||
secret = None
|
||||
|
||||
@property
|
||||
def cachekey_key(self):
|
||||
return '{0}_key_{1}'.format(ACCESS_NAME, self.id)
|
||||
|
||||
@property
|
||||
def cachekey_secret(self):
|
||||
return '{0}_secret_{1}'.format(ACCESS_NAME, self.id)
|
||||
|
||||
@property
|
||||
def authed(self):
|
||||
return bool(self.id and self.key and self.secret)
|
||||
|
||||
@classmethod
|
||||
def factory(cls, key=None, secret=None):
|
||||
s = cls()
|
||||
s.id = uuid4().hex
|
||||
s.key = key
|
||||
s.secret = secret
|
||||
return s
|
||||
|
||||
@classmethod
|
||||
def from_request(cls, request):
|
||||
s = cls()
|
||||
s.id = request.COOKIES.get(ACCESS_NAME)
|
||||
s.key = cache.get(s.cachekey_key)
|
||||
s.secret = cache.get(s.cachekey_secret)
|
||||
return s
|
||||
|
||||
def delete(self, response):
|
||||
response.delete_cookie(REDIRECT_NAME)
|
||||
response.delete_cookie(ACCESS_NAME)
|
||||
cache.delete(self.cachekey_key)
|
||||
cache.delete(self.cachekey_secret)
|
||||
self.id = None
|
||||
self.key = None
|
||||
self.secret = None
|
||||
|
||||
def save(self, response):
|
||||
cache.set(self.cachekey_key, self.key, MAX_AGE)
|
||||
cache.set(self.cachekey_secret, self.secret, MAX_AGE)
|
||||
response.set_cookie(REDIRECT_NAME, '1', max_age=MAX_AGE)
|
||||
response.set_cookie(ACCESS_NAME, self.id, max_age=MAX_AGE, secure=True)
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
import logging
|
||||
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
|
||||
from . import *
|
||||
import tweepy
|
||||
|
||||
|
||||
log = logging.getLogger('k')
|
||||
|
||||
|
||||
class SessionMiddleware(object):
|
||||
|
||||
def process_request(self, request):
|
||||
if getattr(request, 'twitter', False):
|
||||
return
|
||||
|
||||
request.twitter = Session.from_request(request)
|
||||
|
||||
auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY,
|
||||
settings.TWITTER_CONSUMER_SECRET,
|
||||
ssl_url(request))
|
||||
|
||||
if request.GET.get('twitter_delete_auth'):
|
||||
request.twitter = Session.factory()
|
||||
|
||||
elif request.twitter.authed:
|
||||
auth.set_access_token(request.twitter.key, request.twitter.secret)
|
||||
request.twitter.api = tweepy.API(auth)
|
||||
|
||||
else:
|
||||
|
||||
verifier = request.GET.get('oauth_verifier')
|
||||
if verifier:
|
||||
# We are completing an OAuth login
|
||||
|
||||
request_key = request.COOKIES.get(REQUEST_KEY_NAME)
|
||||
request_secret = request.COOKIES.get(REQUEST_SECRET_NAME)
|
||||
|
||||
if request_key and request_secret:
|
||||
auth.set_request_token(request_key, request_secret)
|
||||
|
||||
try:
|
||||
auth.get_access_token(verifier)
|
||||
except tweepy.TweepError:
|
||||
log.warning('Tweepy Error with verifier token')
|
||||
pass
|
||||
else:
|
||||
request.twitter = Session.factory(
|
||||
auth.access_token.key, auth.access_token.secret)
|
||||
|
||||
elif request.GET.get('twitter_auth_request'):
|
||||
# We are requesting Twitter auth
|
||||
|
||||
try:
|
||||
redirect_url = auth.get_authorization_url()
|
||||
except tweepy.TweepError:
|
||||
log.warning('Tweepy error while getting authorization url')
|
||||
else:
|
||||
response = http.HttpResponseRedirect(redirect_url)
|
||||
response.set_cookie(REQUEST_KEY_NAME, auth.request_token.key,
|
||||
max_age=MAX_AGE, secure=True)
|
||||
response.set_cookie(REQUEST_SECRET_NAME, auth.request_token.secret,
|
||||
max_age=MAX_AGE, secure=True)
|
||||
return response
|
||||
|
||||
|
||||
def process_response(self, request, response):
|
||||
if getattr(request, 'twitter', False):
|
||||
if request.GET.get('twitter_delete_auth'):
|
||||
request.twitter.delete(response)
|
||||
|
||||
if request.twitter.authed:
|
||||
response.delete_cookie(REQUEST_KEY_NAME)
|
||||
response.delete_cookie(REQUEST_SECRET_NAME)
|
||||
request.twitter.save(response)
|
||||
|
||||
return response
|
|
@ -26,6 +26,7 @@ body {
|
|||
}
|
||||
#speach-bubbles {
|
||||
padding: 60px 0 15px 25px;
|
||||
float: left;
|
||||
}
|
||||
#speach-bubbles li {
|
||||
list-style-type: none;
|
||||
|
@ -46,7 +47,8 @@ body {
|
|||
#tweetcontainer {
|
||||
border: 1px solid #f5f5f5;
|
||||
padding: 2px;
|
||||
clear: both;
|
||||
float: left;
|
||||
clear: left;
|
||||
}
|
||||
#tweetcontainer h2 {
|
||||
margin: 0px;
|
||||
|
@ -93,24 +95,21 @@ body {
|
|||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
#tweets li span {
|
||||
display: block;
|
||||
}
|
||||
#tweets li span {
|
||||
#tweets li .twittername {
|
||||
color: #307fc1;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
#tweets li span .time {
|
||||
#tweets li .time {
|
||||
float: right;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
color: #afaba3;
|
||||
}
|
||||
#tweets li span .text {
|
||||
#tweets li .text {
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
color: #69645b;
|
||||
|
@ -281,7 +280,7 @@ body {
|
|||
display: inline;
|
||||
}
|
||||
|
||||
#reply .submit-message {
|
||||
#submit-message {
|
||||
display: none;
|
||||
background: url('../img/customercare/reply-check.png') right top no-repeat;
|
||||
font-weight: bold;
|
||||
|
|
|
@ -67,4 +67,18 @@
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#side-getinvolved {
|
||||
background: transparent url('../img/side-getinvolved-bg.png') no-repeat top left;
|
||||
margin-top: 140px;
|
||||
padding-top: 20px;
|
||||
color: #999186;
|
||||
}
|
||||
|
||||
#side-getinvolved h3 {
|
||||
color: #1E4262;
|
||||
font-size: 150%;
|
||||
}
|
||||
#side-getinvolved p {
|
||||
padding-top: 10px;
|
||||
font-size: 115%;
|
||||
}
|
||||
|
|
До Ширина: | Высота: | Размер: 4.8 KiB После Ширина: | Высота: | Размер: 4.8 KiB |
До Ширина: | Высота: | Размер: 4.2 KiB После Ширина: | Высота: | Размер: 4.2 KiB |
|
@ -1,56 +1,68 @@
|
|||
var has_twitter_access = false;
|
||||
// cookie names are duplicated in apps/customercare/views.py
|
||||
if ($.cookie('custcare_twitter_access_id'))
|
||||
has_twitter_access = true;
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
$('.reply-message').NobleCount('.character-counter');
|
||||
$('.reply-message').NobleCount('.character-counter');
|
||||
|
||||
$('.reply-message').autoPlaceholderText();
|
||||
$('.reply-message').autoPlaceholderText();
|
||||
|
||||
$('#accordion').accordion({
|
||||
'icons': false,
|
||||
'autoHeight': false,
|
||||
});
|
||||
$('#accordion').accordion({
|
||||
'icons': false,
|
||||
'autoHeight': false,
|
||||
});
|
||||
|
||||
$('.tweet').click(function() {
|
||||
var twitter_modal = $('#twitter-modal');
|
||||
if (twitter_modal.attr('data-authed') == 'False') {
|
||||
twitter_modal.dialog({
|
||||
'modal': 'true',
|
||||
'position': 'top',
|
||||
});
|
||||
twitter_modal.find('.cancel').click(function(e) {
|
||||
twitter_modal.dialog('close');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var reply_to = $(this).attr('data-reply_to')
|
||||
var avatar_href = $(this).find('.avatar').attr('href');
|
||||
var avatar_img = $(this).find('.avatar img').attr('src');
|
||||
var twittername = $(this).find('.twittername').text();
|
||||
var text = $(this).find('.text').text();
|
||||
|
||||
var modal = $('#reply-modal');
|
||||
modal.find('#reply_to').val(reply_to);
|
||||
modal.find('.avatar').attr('href', avatar_href);
|
||||
modal.find('.avatar img').attr('src', avatar_img);
|
||||
modal.find('.twittername').text(twittername);
|
||||
modal.find('.text').text(text);
|
||||
modal.dialog({
|
||||
'modal': true,
|
||||
'position': 'top',
|
||||
'width': 500,
|
||||
});
|
||||
});
|
||||
|
||||
$('.reply-topic').click(function(e) {
|
||||
snippet = $(this).next('.snippet').text();
|
||||
$('.reply-message').val(snippet);
|
||||
$('.reply-message').trigger('keydown');
|
||||
|
||||
$('.tweet').click(function() {
|
||||
if (!has_twitter_access) {
|
||||
$('#twitter-modal').dialog({
|
||||
'modal': 'true',
|
||||
'position': 'top',
|
||||
});
|
||||
$('#twitter-modal .cancel').click(function(e) {
|
||||
$('#twitter-modal').dialog('close');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var avatar_href = $(this).find('.avatar').attr('href');
|
||||
var avatar_img = $(this).find('.avatar img').attr('src');
|
||||
var twittername = $(this).find('.twittername').text();
|
||||
var text = $(this).find('.text').text();
|
||||
|
||||
var modal = $('#reply-modal');
|
||||
modal.find('.avatar').attr('href', avatar_href);
|
||||
modal.find('.avatar img').attr('src', avatar_img);
|
||||
modal.find('.twittername').text(twittername);
|
||||
modal.find('.text').text(text);
|
||||
modal.dialog({
|
||||
'modal': true,
|
||||
'position': 'top',
|
||||
'width': 500,
|
||||
});
|
||||
});
|
||||
|
||||
$('.reply-topic').click(function(e) {
|
||||
snippet = $(this).next('.snippet').text();
|
||||
$('.reply-message').val(snippet);
|
||||
$('.reply-message').trigger('keydown');
|
||||
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
$('#reply-modal #submit').click(function(e) {
|
||||
var action = $('#reply-modal form').attr('action');
|
||||
var tweet = $('.reply-message').val();
|
||||
var reply_to = $('#reply_to').val();
|
||||
$.post(
|
||||
action,
|
||||
{ 'tweet': tweet, 'reply_to': reply_to },
|
||||
function() {
|
||||
$('#submit-message').show();
|
||||
}
|
||||
);
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -142,6 +142,8 @@ MIDDLEWARE_CLASSES = (
|
|||
|
||||
# TODO: Replace with Kitsune auth.
|
||||
'sumo.middleware.TikiCookieMiddleware',
|
||||
|
||||
'twitter.middleware.SessionMiddleware',
|
||||
)
|
||||
|
||||
# Auth
|
||||
|
@ -189,6 +191,7 @@ INSTALLED_APPS = (
|
|||
'wiki',
|
||||
'gallery',
|
||||
'customercare',
|
||||
'twitter',
|
||||
)
|
||||
|
||||
# Extra apps for testing
|
||||
|
@ -330,7 +333,6 @@ MINIFY_BUNDLES = {
|
|||
'customercare': (
|
||||
'js/libs/jqueryui.min.js',
|
||||
'js/libs/jquery.NobleCount.js',
|
||||
'js/libs/jquery.cookie.js',
|
||||
'js/customercare.js',
|
||||
),
|
||||
},
|
||||
|
|
|
@ -14,4 +14,12 @@
|
|||
<li><a href="{{ settings.LOGIN_URL }}">{{ _('Log In') }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<div id="side-getinvolved">
|
||||
<h3>Want to get involved?</h3>
|
||||
<p>Did you know that most of the content on Firefox Support was written by volunteers?</p>
|
||||
<p><a href="http://support.mozilla.com/en-US/kb/Providing+Forum+Support">Find out how to contribute</a>,<br>or <a href="">log in</a>.</p>
|
||||
<p><a href="/"><img src="{{ MEDIA_URL }}img/sumo-logo.png" class="sumo-logo"></a></p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
Загрузка…
Ссылка в новой задаче