diff options
author | ashwinishinde | 2015-06-19 15:39:01 +0530 |
---|---|---|
committer | ashwinishinde | 2015-06-19 15:39:01 +0530 |
commit | bf20363adec8afed8d8e10ad7e0b641c51568cff (patch) | |
tree | 324a5ee334456c9ce602ee07aa43912ce5590adb | |
parent | 23c44aa78fcf3c015dd4bcaf5b7e8a223e1b6950 (diff) | |
download | FOSSEE-Forum-bf20363adec8afed8d8e10ad7e0b641c51568cff.tar.gz FOSSEE-Forum-bf20363adec8afed8d8e10ad7e0b641c51568cff.tar.bz2 FOSSEE-Forum-bf20363adec8afed8d8e10ad7e0b641c51568cff.zip |
Subject: Added Vote Functionality
Description:
1) Implementation of vote for question
2) Added CSS for vote-up and vope-down
-rw-r--r-- | forums/settings.py | 7 | ||||
-rw-r--r-- | forums/urls.py | 1 | ||||
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | static/website/css/main.css | 83 | ||||
-rw-r--r-- | static/website/images/down1-off.png | bin | 0 -> 483 bytes | |||
-rw-r--r-- | static/website/images/down1-on.png | bin | 0 -> 285 bytes | |||
-rw-r--r-- | static/website/images/up1-off.png | bin | 0 -> 458 bytes | |||
-rw-r--r-- | static/website/images/up1-on.png | bin | 0 -> 274 bytes | |||
-rw-r--r-- | static/website/templates/base.html | 9 | ||||
-rw-r--r-- | static/website/templates/get-question.html | 214 | ||||
-rw-r--r-- | static/website/templates/questions.html | 2 | ||||
-rw-r--r-- | static/website/templates/search.html | 1 | ||||
-rw-r--r-- | website/models.py | 10 | ||||
-rw-r--r-- | website/urls.py | 4 | ||||
-rw-r--r-- | website/views.py | 58 |
15 files changed, 282 insertions, 108 deletions
diff --git a/forums/settings.py b/forums/settings.py index b556319..c21d2e0 100644 --- a/forums/settings.py +++ b/forums/settings.py @@ -142,6 +142,8 @@ INSTALLED_APPS = ( 'compressor', 'debug_toolbar', 'captcha', + 'googlesearch', + 'gtm', #'migrate_spoken', ) @@ -177,11 +179,12 @@ LOGGING = { TEMPLATE_CONTEXT_PROCESSORS += ( 'django.core.context_processors.request', 'website.context_processors.admin_processor', + ) COMPRESS_ROOT = PROJECT_DIR + "/static/" -COMPRESS_ENABLED = True # disable in production Env -HTML_MINIFY = True # disable in production Env +COMPRESS_ENABLED = False # disable in production Env +HTML_MINIFY = False # disable in production Env HTML_MINIFY = HTML_MINIFY RECAPTCHA_PUBLIC_KEY = '6LemngMTAAAAAAC0Fkv0CQcavkTIIJ3LTDzi9gMq' diff --git a/forums/urls.py b/forums/urls.py index 31c679b..4584b66 100644 --- a/forums/urls.py +++ b/forums/urls.py @@ -23,4 +23,5 @@ urlpatterns = patterns('', url(r"^accounts/confirm/(?P<confirmation_code>\w+)/(?P<username>[\w. @-]+)/$", 'forums.views.confirm', name='confirm'), url(r"^accounts/profile/(?P<username>[\w. @-]+)/$", 'forums.views.account_profile', name='profile'), url(r"^accounts/view-profile/(?P<username>[\w. @-]+)/$", 'forums.views.account_view_profile', name='view_profile'), + url(r'^search/', include('googlesearch.urls')), ) diff --git a/requirements.txt b/requirements.txt index e7beaee..1adb4bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ html5lib==1.0b3 six==1.6.1 sqlparse==0.1.11 wsgiref==0.1.2 +django-googlesearch diff --git a/static/website/css/main.css b/static/website/css/main.css index 20f220a..774871e 100644 --- a/static/website/css/main.css +++ b/static/website/css/main.css @@ -248,6 +248,8 @@ table .question a{ margin: 5px 0px; } #content .answer .body { + padding-bottom: 10px; + ,, } #content .answer .user { padding: 2px 7px; @@ -401,3 +403,84 @@ table .question a{ font-size: 22px; overflow: hidden; } + + +.votes { + color: rgb(85, 85, 85); + padding-top: 8px; + padding-bottom: 8px; + text-align: center; +} + +.vote span { + display: block; + color: rgb(128, 129, 133); + font-weight: normal; +} + +.vote-count-post { + display: block; + font-size: 17px; + +} + +.votecell { + background:#ffffff; + + float:left; + width:60px; + height:100px; + margin-right:5px; + padding:0; + border-radius: 3px; +} + +.post-text { + word-wrap: break-word; + width: 660px; + font-size: 107%; + margin-bottom: 5px; + margin-right: 5px; + margin-right-value: 5px; + margin-right-ltr-source: physical; + margin-right-rtl-source: physical; + line-height: 130%; +} + +.post-taglist { + margin-bottom: 10px; + clear: both; +} + +.vote { + text-align: center; +} + + +.vote-up-off, .vote-up-on, .vote-down-off, .vote-down-on, .star-on, .star-off, .comment-up-off, .comment-up-on, .comment-flag, .flag-off, .vote-accepted-off, .vote-accepted-on { + text-indent: -9999em; + font-size: 1px; +} + +.vote-up-off, .vote-up-on, .vote-down-off, .vote-down-on, .star-on, .star-off { + background-position: 0px -265px; + display: block; + margin: 0px auto; + margin-top: 0px; + margin-right-value: auto; + margin-bottom: 0px; + margin-left-value: auto; + margin-left-ltr-source: physical; + margin-left-rtl-source: physical; + margin-right-ltr-source: physical; + margin-right-rtl-source: physical; + width: 41px; + height: 25px; +} + + +.space { + padding-top: 20px; +} + + diff --git a/static/website/images/down1-off.png b/static/website/images/down1-off.png Binary files differnew file mode 100644 index 0000000..c720b49 --- /dev/null +++ b/static/website/images/down1-off.png diff --git a/static/website/images/down1-on.png b/static/website/images/down1-on.png Binary files differnew file mode 100644 index 0000000..84f1a23 --- /dev/null +++ b/static/website/images/down1-on.png diff --git a/static/website/images/up1-off.png b/static/website/images/up1-off.png Binary files differnew file mode 100644 index 0000000..9fecb01 --- /dev/null +++ b/static/website/images/up1-off.png diff --git a/static/website/images/up1-on.png b/static/website/images/up1-on.png Binary files differnew file mode 100644 index 0000000..f756707 --- /dev/null +++ b/static/website/images/up1-on.png diff --git a/static/website/templates/base.html b/static/website/templates/base.html index b174d69..b5d087b 100644 --- a/static/website/templates/base.html +++ b/static/website/templates/base.html @@ -21,6 +21,15 @@ {% endcompress %} </head> <body> + <!-- Google Tag Manager --> +<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-NQBKKJ" +height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> +<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': +new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], +j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= +'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); +})(window,document,'script','dataLayer','GTM-NQBKKJ');</script> +<!-- End Google Tag Manager --> <div id="page-wrapper"> <div id="header-wrapper"> <div id="header-inner"> diff --git a/static/website/templates/get-question.html b/static/website/templates/get-question.html index c31259b..7c93803 100644 --- a/static/website/templates/get-question.html +++ b/static/website/templates/get-question.html @@ -9,17 +9,47 @@ {% endblock %} {% block content %} + <span class="saving hideme">saving . . .</span> <span class="saved hideme">saved</span> <div class="row"> + + + <div class="question-wrapper col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="question {% if user|can_edit:question %}editable{% endif %}"> + <!-- VOTE CELL --> + + + + <div class="votecell" style="padding-top: 0.0cm;"> + <div class="vote"> + {% ifequal thisUserUpvote 0 %} + <img src="{% static 'website/images/up1-off.png' %}" class="vote-up" title="This question shows research effort; it is useful and clear"/> + {% else %} + <img src="{% static 'website/images/up1-on.png' %}" class="vote-up selected" title="This question shows research effort; it is useful and clear"/> + {% endifequal %} + + <span class="vote-count-post" id="show_vote_count"> + {{ net_count }} + </span> + + {% ifequal thisUserDownvote 0 %} + <img src="{% static 'website/images/down1-off.png' %}" class="vote-down" title="This question does not show any research effort; it is not useful and unclear"/> + {% else %} + <img src="{% static 'website/images/down1-on.png' %}" class="vote-down selected" title="This question does not show any research effort; it is not useful and unclear"/> + {% endifequal %} + </div> + + </div> <div class="title {% if user|can_edit:question %}title-editable{% endif %}"> {{ question.title }} </div> <div id="title-edit" class="hideme"> + + <h5>Title</h5> <input class="form-control" type="text"> <h5>Question</h5> @@ -62,10 +92,13 @@ {{ question.user }} </span> </span> + </div> <!-- /.question --> </div> + </div> <!-- /.row --> + <h4><u>Answers:</u></h4> <div id="answerPanelWrapper" style="display:none;"> <div id="answerNicPanel" style="display:none;"></div> @@ -207,80 +240,7 @@ <div class="modal-body"> <h5>The Current question is under:</h5> - {{ question.category }} <!-- - - <h5>Move the question to:</h5> - <div class="row"> - <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4"> - <select id="id_category" class="form-control"> - <option value="Advanced-C++">Advanced-C++</option> - <option value="BASH">BASH</option> - <option value="Blender">Blender</option> - <option value="C-and-C++">C-and-C++</option> - <option value="CellDesigner">CellDesigner</option> - <option value="Digital-Divide">Digital-Divide</option> - <option value="Drupal">Drupal</option> - <option value="Firefox">Firefox</option> - <option value="GChemPaint">GChemPaint</option> - <option value="Geogebra">Geogebra</option> - <option value="GeoGebra-for-Engineering-drawing">GeoGebra-for-Engineering-drawing</option> - <option value="GIMP">GIMP</option> - <option value="GNS3">GNS3</option> - <option value="GSchem">GSchem</option> - <option value="Inkscape">Inkscape</option> - <option value="Java">Java</option> - <option value="Java-Business-Application">Java-Business-Application</option> - <option value="KiCad">KiCad</option> - <option value="KTouch">KTouch</option> - <option value="KTurtle">KTurtle</option> - <option value="LaTeX">LaTeX</option> - <option value="LibreOffice-Suite-Base">LibreOffice-Suite-Base</option> - <option value="LibreOffice-Suite-Calc">LibreOffice-Suite-Calc</option> - <option value="LibreOffice-Suite-Draw">LibreOffice-Suite-Draw</option> - <option value="LibreOffice-Suite-Impress">LibreOffice-Suite-Impress</option> - <option value="LibreOffice-Suite-Math">LibreOffice-Suite-Math</option> - <option value="LibreOffice-Suite-Writer">LibreOffice-Suite-Writer</option> - <option value="Linux">Linux</option> - <option value="Netbeans">Netbeans</option> - <option value="Ngspice">Ngspice</option> - <option value="OpenFOAM">OpenFOAM</option> - <option value="Orca">Orca</option> - <option value="Oscad">Oscad</option> - <option value="PERL">PERL</option> - <option value="PHP-and-MySQL">PHP-and-MySQL</option> - <option value="Python">Python</option> - <option value="Python-Old-Version">Python-Old-Version</option> - <option value="QCad">QCad</option> - <option value="R">R</option> - <option value="Ruby">Ruby</option> - <option value="Scilab">Scilab</option> - <option value="Selenium">Selenium</option> - <option value="Single-Board-Heater-System">Single-Board-Heater-System</option> - <option value="Spoken-Tutorial-Technology">Spoken-Tutorial-Technology</option> - <option value="Step">Step</option> - <option value="Thunderbird">Thunderbird</option> - <option value="Tux-Typing">Tux-Typing</option> - <option value="What-is-Spoken-Tutorial">What-is-Spoken-Tutorial</option> - <option value="Xfig">Xfig</option> - <option value="General">General FOSS</option> - </select> - </div> - <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4"> - <select id="id_tutorial" class="form-control" disabled> - <option value="None">Select a Tutorial</option> - </select> - </div> - <div class="col-lg-2 col-md-2 col-sm-2 col-xs-2"> - <select id="id_minute_range" class="form-control" disabled> - <option value="None">min</option> - </select> - </div> - <div class="col-lg-2 col-md-2 col-sm-2 col-xs-2"> - <select id="id_second_range" class="form-control" disabled> - <option value="None">sec</option> - </select> - </div> - </div> <!-- /.row --> + {{ question.category }} <hr> <div class="pull-right"> <a id="question-details-ok" class="btn btn-sm btn-success hideme" data-qid={{ question.id }} href="#">Ok</a> @@ -291,8 +251,14 @@ </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> +<!-- AJAX SCRIPT --> + + -<script src="{% static 'website/js/nicEdit.js' %}" type="text/javascript"></script> +{% endblock %} + +{% block javascript %} + <script src="{% static 'website/js/nicEdit.js' %}" type="text/javascript"></script> <script type="text/javascript"> bkLib.onDomLoaded(function() { new nicEditor({ @@ -301,34 +267,82 @@ }).panelInstance('id_body'); }); </script> -{% endblock %} + <!-- <script src="{% static 'website/js/thread-user.js' %}"></script> + <script src="{% static 'website/js/custom.js' %}"></script> --> + <script type="text/javascript"> -{% block javascript %} - <script src="{% static 'website/js/thread-user.js' %}"></script> - <script src="{% static 'website/js/custom.js' %}"></script> - <script> $(document).ready(function() { - $vote = $(".vote"); + $.ajaxSetup({ + data: {csrfmiddlewaretoken: '{{ csrf_token }}' }, + }); + + $('div.vote img.vote-up').click(function() { + - $vote.click(function() { - if($(this).hasClass("upvote")) { - /* handle upvote click event */ - if($(this).hasClass("voted")) { - $(this).removeClass("voted"); - } else { - $(this).addClass("voted"); - $(this).siblings(".downvote").removeClass("voted"); - } - } else { - /* handle downvote click event */ - if($(this).hasClass("voted")) { - $(this).removeClass("voted"); - } else { - $(this).addClass("voted"); - $(this).siblings(".upvote").removeClass("voted"); - } - } - }); + + var id = {{ question.pk }}; + var vote_type = 'up'; + + /* USER HAS ALREADY VOTED */ + + if ($(this).hasClass('selected')) { + var vote_action = 'recall-vote' + + $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) { + + $('img.vote-up').removeAttr('src') + .attr('src', '{% static 'website/images/up1-off.png' %}') + .removeClass('selected'); + $('div.vote span.vote-count-post').html(response); + }); + } + /* USER WISHES TO VOTE */ + else { + var vote_action = 'vote' + $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) { + + $('img.vote-up').removeAttr('src') + .attr('src', '{% static 'website/images/up1-on.png' %}') + .addClass('selected'); + $('div.vote span.vote-count-post').html(response); + }); + } }); + + /* DOWNVOTE */ + + $('div.vote img.vote-down').click(function() { + + var id = {{ question.pk }}; + var vote_type = 'down'; + + /* USER HAS ALREADY DOWN-VOTED */ + + if ($(this).hasClass('selected')) { + var vote_action = 'recall-vote' + + $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) { + + $('img.vote-down').removeAttr('src') + .attr('src', '{% static 'website/images/down1-off.png' %}') + .removeClass('selected'); + $('div.vote span.vote-count-post').html(response); + }); + } + /* USER WISHES TO VOTE */ + else { + var vote_action = 'vote' + $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) { + + $('img.vote-down').removeAttr('src') + .attr('src', '{% static 'website/images/down1-on.png' %}') + .addClass('selected'); + $('div.vote span.vote-count-post').html(response); + }); + } + }); + }); + </script> + {% endblock %} diff --git a/static/website/templates/questions.html b/static/website/templates/questions.html index 98d016d..96f596d 100644 --- a/static/website/templates/questions.html +++ b/static/website/templates/questions.html @@ -49,8 +49,8 @@ <td> {{ question.answer_set.count }} </td> + - <td> <span class="title" data-toggle="tooltip" data-placement="top" title="{{ question.user }}"> {{ question.user|truncatechars:10 }} </span> diff --git a/static/website/templates/search.html b/static/website/templates/search.html index a7ac917..c374b47 100644 --- a/static/website/templates/search.html +++ b/static/website/templates/search.html @@ -1,4 +1,5 @@ {% extends 'website/templates/base.html' %} +{% load i18n googlesearch_inclusion_tags %} {% load static %} {% block content %} diff --git a/website/models.py b/website/models.py index c7e43aa..b0a9fc3 100644 --- a/website/models.py +++ b/website/models.py @@ -23,13 +23,14 @@ class Issue(models.Model): class Question(models.Model): user = models.ForeignKey(User) category = models.ForeignKey(FossCategory) - #tutorial = models.ForeignKey(Issue) title = models.CharField(max_length=200) body = models.TextField() date_created = models.DateTimeField(auto_now_add=True) date_modified = models.DateTimeField(auto_now=True) views = models.IntegerField(default=1) - # votes = models.IntegerField(default=0) + userUpVotes = models.ManyToManyField(User, blank=True, related_name='postUpVotes') + userDownVotes = models.ManyToManyField(User, blank=True, related_name='postDownVotes') + num_votes = models.IntegerField(default=0) def __unicode__(self): return '%s' % (self.user) @@ -53,7 +54,10 @@ class Answer(models.Model): body = models.TextField() date_created = models.DateTimeField(auto_now_add=True) date_modified = models.DateTimeField(auto_now=True) - # votes = models.IntegerField(default=0) + #userUpVotes = models.ManyToManyField(User, blank=True, related_name='postUpVotes') + #userDownVotes = models.ManyToManyField(User, blank=True, related_name='postDownVotes') + upvotes = models.IntegerField(default=0) + num_votes = models.IntegerField(default=0) def user(self): user = User.objects.get(id=self.uid) diff --git a/website/urls.py b/website/urls.py index 1c7d75e..71588af 100644 --- a/website/urls.py +++ b/website/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import patterns, include, url +from website import views urlpatterns = patterns('', url(r'^$', 'website.views.home', name='home'), @@ -9,6 +10,7 @@ urlpatterns = patterns('', url(r'^answer-comment/$', 'website.views.answer_comment', name='answer_comment'), url(r'^filter/(?P<category>[^/]+)/$', 'website.views.filter', name='filter'), url(r'^filter/(?P<category>[^/]+)/$', 'website.views.filter', name='filter'), + #url(r'^filter/(?P<category>[^/]+)/(?P<tutorial>[^/]+)/(?P<minute_range>[^/]+)/$', 'website.views.filter', name='filter'), #url(r'^filter/(?P<category>[^/]+)/(?P<tutorial>[^/]+)/(?P<minute_range>[^/]+)/(?P<second_range>[^/]+)/$', 'website.views.filter', name='filter'), url(r'^new-question/$', 'website.views.new_question', name='new_question'), @@ -18,7 +20,7 @@ urlpatterns = patterns('', url(r'^clear-notifications/$', 'website.views.clear_notifications', name='clear_notifications'), url(r'^search/$', 'website.views.search', name='search'), url(r'^unanswered-notification/$', 'website.views.unanswered_notification', name='unanswered_notification'), - + url(r'^vote_post/$', 'website.views.vote_post', name='vote_post'), # Ajax helpers url(r'^ajax-tutorials/$', 'website.views.ajax_tutorials', name='ajax_tutorials'), url(r'^ajax-duration/$', 'website.views.ajax_duration', name='ajax_duration'), diff --git a/website/views.py b/website/views.py index f80ee13..c9a2a5f 100644 --- a/website/views.py +++ b/website/views.py @@ -55,10 +55,17 @@ def get_question(request, question_id=None, pretty_url=None): return HttpResponseRedirect('/question/'+ question_id + '/' + pretty_title) answers = question.answer_set.all() form = AnswerQuestionForm() + thisuserupvote = question.userUpVotes.filter(id=request.user.id).count() + thisuserdownvote = question.userDownVotes.filter(id=request.user.id).count() + + net_count = question.userUpVotes.count() - question.userDownVotes.count() context = { 'question': question, 'answers': answers, - 'form': form + 'form': form, + 'thisUserUpvote': thisuserupvote, + 'thisUserDownvote': thisuserdownvote, + 'net_count': net_count } context.update(csrf(request)) @@ -292,6 +299,55 @@ def new_question(request): context.update(csrf(request)) return render(request, 'website/templates/new-question.html', context) + +def vote_post(request): + + print "post" + post_id = int(request.POST.get('id')) + + vote_type = request.POST.get('type') + vote_action = request.POST.get('action') + print post_id + cur_post = get_object_or_404(Question, id=post_id) + + thisuserupvote = cur_post.userUpVotes.filter(id=request.user.id).count() + thisuserdownvote = cur_post.userDownVotes.filter(id=request.user.id).count() + + initial_votes = cur_post.userUpVotes.count() - cur_post.userDownVotes.count() + + # print "User Initial Upvote and Downvote: %d %d %s " % (thisuserupvote, thisuserdownvote, vote_action) + + #This loop is for voting + if vote_action == 'vote': + if (thisuserupvote == 0) and (thisuserdownvote == 0): + if vote_type == 'up': + cur_post.userUpVotes.add(request.user) + elif vote_type == 'down': + cur_post.userDownVotes.add(request.user) + else: + return HttpResponse("Error: Unknown vote-type passed.") + else: + return HttpResponse(initial_votes) + #This loop is for canceling vote + elif vote_action == 'recall-vote': + if (vote_type == 'up') and (thisuserupvote == 1): + cur_post.userUpVotes.remove(request.user) + elif (vote_type == 'down') and (thisuserdownvote == 1): + cur_post.userDownVotes.remove(request.user) + else: + # "Error - Unknown vote type or no vote to recall" + return HttpResponse(initial_votes) + else: + return HttpResponse("Error: Bad Action.") + + num_votes = cur_post.userUpVotes.count() - cur_post.userDownVotes.count() + cur_post.num_votes = num_votes + cur_post.save() + + print "Num Votes: %s" % num_votes + + return HttpResponse(num_votes) + # Notification Section @login_required def user_questions(request, user_id): |