├── web ├── __init__.py ├── blog │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_post_is_announcement.py │ │ └── 0001_initial.py │ ├── sitemaps.py │ ├── urls.py │ ├── admin.py │ ├── models.py │ └── views.py ├── main │ ├── __init__.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── newsfeed │ ├── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── create_initial_newsfeed.py │ ├── constants.py │ ├── urls.py │ ├── utils.py │ ├── views.py │ └── models.py ├── premises │ ├── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── reports_to_gml.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0009_merge.py │ │ ├── 0005_auto_20141012_0301.py │ │ ├── 0003_premise_text.py │ │ ├── 0013_premise_child_count.py │ │ ├── 0002_auto_20141009_1922.py │ │ ├── 0015_premise_collapsed.py │ │ ├── 0006_contention_is_published.py │ │ ├── 0014_premise_max_sibling_count.py │ │ ├── 0019_premise_date_creation.py │ │ ├── 0018_premise_supporters.py │ │ ├── 0012_auto_20141027_1905.py │ │ ├── 0011_auto_20141027_1845.py │ │ ├── 0020_auto_20141105_0153.py │ │ ├── 0021_auto_20141107_0059.py │ │ ├── 0004_auto_20141012_0056.py │ │ ├── 0008_auto_20141023_0133.py │ │ ├── 0008_auto_20141022_1951.py │ │ ├── 0017_auto_20141030_0353.py │ │ ├── 0010_auto_20141027_0204.py │ │ ├── 0001_initial.py │ │ ├── 0016_report_fallacy_type.py │ │ └── 0007_auto_20141020_2215.py │ ├── templatetags │ │ ├── __init__.py │ │ └── premise_tags.py │ ├── tests.py │ ├── constants.py │ ├── utils.py │ ├── signals.py │ ├── admin.py │ ├── managers.py │ ├── mixins.py │ ├── fallacies.json │ ├── forms.py │ ├── urls.py │ ├── models.py │ └── views.py ├── profiles │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_notification.py │ │ └── 0001_initial.py │ ├── management │ │ ├── commands │ │ │ ├── __init__.py │ │ │ └── update_premises.py │ │ └── __init__.py │ ├── signals.py │ ├── admin.py │ ├── urls.py │ ├── mixins.py │ ├── forms.py │ ├── views.py │ └── models.py ├── templates │ ├── blog │ │ ├── base.html │ │ ├── pagination.html │ │ ├── detail.html │ │ ├── index.html │ │ ├── post.html │ │ └── latest.html │ ├── premises │ │ ├── examples │ │ │ ├── premise_source.html │ │ │ ├── premise.html │ │ │ ├── sources.html │ │ │ ├── fallacy.html │ │ │ ├── owner.html │ │ │ ├── premise_type.html │ │ │ └── contention.html │ │ ├── actions.html │ │ ├── edit_contention.html │ │ ├── edit_premise.html │ │ ├── report.html │ │ ├── new_contention.html │ │ ├── contention.html │ │ ├── new_premise.html │ │ ├── tree_view.html │ │ └── contention_detail.html │ ├── newsfeed │ │ ├── contention.html │ │ ├── following.html │ │ ├── fallacy.html │ │ └── premise.html │ ├── tos.html │ ├── about.html │ ├── notifications │ │ ├── followed.html │ │ ├── reported-as-fallacy.html │ │ ├── supported-a-premise.html │ │ ├── added-premise-for-contention.html │ │ └── added-premise-for-premise.html │ ├── home.html │ ├── auth │ │ ├── complete.html │ │ ├── login.html │ │ ├── update.html │ │ ├── register.html │ │ └── profile.html │ ├── notifications.html │ ├── profiles │ │ └── social_providers.html │ ├── partials │ │ └── notifications.html │ ├── infinity-pagination.html │ ├── tabs.html │ ├── newsfeed.html │ ├── about.md │ ├── index.html │ ├── base.html │ └── tos.md ├── static │ ├── img │ │ ├── logo.png │ │ ├── zoom-in.png │ │ └── zoom-out.png │ ├── fonts │ │ ├── AmericanTypewriter.ttf │ │ └── AmericanTypewriterBold.ttf │ ├── js │ │ ├── lib │ │ │ ├── class.js │ │ │ ├── underscore-template.js │ │ │ └── hipo.infinity-scroll.js │ │ └── main.js │ └── css │ │ ├── h5bp.css │ │ └── normalize.css └── manage.py ├── .gitignore ├── requirements.pip ├── LICENSE └── README.md /web/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/blog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/newsfeed/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/premises/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/profiles/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/blog/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/newsfeed/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/premises/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/premises/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/premises/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/profiles/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/newsfeed/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/premises/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/profiles/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/templates/blog/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | -------------------------------------------------------------------------------- /web/profiles/management/__init__.py: -------------------------------------------------------------------------------- 1 | __author__='fatiherikli' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite3 2 | settings_local.py 3 | *.pyc 4 | .DS_Store 5 | *.gml 6 | -------------------------------------------------------------------------------- /web/premises/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /web/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fg/arguman.org/master/web/static/img/logo.png -------------------------------------------------------------------------------- /web/static/img/zoom-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fg/arguman.org/master/web/static/img/zoom-in.png -------------------------------------------------------------------------------- /web/static/img/zoom-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fg/arguman.org/master/web/static/img/zoom-out.png -------------------------------------------------------------------------------- /web/templates/premises/examples/premise_source.html: -------------------------------------------------------------------------------- 1 | Örnek: T.C. Karayolları Trafik Kanunu 2918/46. Maddesi -------------------------------------------------------------------------------- /web/static/fonts/AmericanTypewriter.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fg/arguman.org/master/web/static/fonts/AmericanTypewriter.ttf -------------------------------------------------------------------------------- /web/newsfeed/constants.py: -------------------------------------------------------------------------------- 1 | NEWS_TYPE_CONTENTION = 0 2 | NEWS_TYPE_PREMISE = 1 3 | NEWS_TYPE_FALLACY = 2 4 | NEWS_TYPE_FOLLOWING = 3 5 | -------------------------------------------------------------------------------- /web/static/fonts/AmericanTypewriterBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fg/arguman.org/master/web/static/fonts/AmericanTypewriterBold.ttf -------------------------------------------------------------------------------- /web/templates/premises/examples/premise.html: -------------------------------------------------------------------------------- 1 | Örnek: Bisiklet sürücüsü karayolunda en sağ şeridi kullanır ve diğer taşıtlar ile aynı sorumlulukla hareket eder. -------------------------------------------------------------------------------- /web/templates/newsfeed/contention.html: -------------------------------------------------------------------------------- 1 | bir argüman oluşturdu: 2 | {{ related_object.title }} 3 | -------------------------------------------------------------------------------- /web/templates/tos.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 | {{ content|safe }} 6 |
7 | {% endblock %} -------------------------------------------------------------------------------- /web/templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 | {{ content|safe }} 6 |
7 | {% endblock %} -------------------------------------------------------------------------------- /web/templates/notifications/followed.html: -------------------------------------------------------------------------------- 1 | {{ notification.sender }} sizi takip ediyor. -------------------------------------------------------------------------------- /web/premises/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | FEATURED_CONTENT_COUNT = 50 3 | NEWS_CONTENT_COUNT = 50 4 | UPDATED_CONTENT_COUNT = 50 5 | 6 | MAX_PREMISE_CONTENT_LENGTH = 300 7 | -------------------------------------------------------------------------------- /web/templates/newsfeed/following.html: -------------------------------------------------------------------------------- 1 | {{ related_object.username }} 2 | kişisini takip ediyor. 3 | -------------------------------------------------------------------------------- /web/profiles/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | follow_done = Signal(providing_args=["follower", "following"]) 4 | unfollow_done = Signal(providing_args=["follower", "following"]) -------------------------------------------------------------------------------- /web/templates/premises/examples/sources.html: -------------------------------------------------------------------------------- 1 | Argümanın kaynağı. Bir URL, kitap adı ya da dergi adı olabilir. 2 | Bu alan önemlidir, kaynaksız ve tartışmalı bir argüman/önerme yayından kaldırılır. -------------------------------------------------------------------------------- /web/templates/blog/pagination.html: -------------------------------------------------------------------------------- 1 | {% if is_paginated %} 2 | {% if page_obj.has_next %} 3 | Show more 4 | {% endif %} 5 | {% endif %} -------------------------------------------------------------------------------- /web/templates/premises/examples/fallacy.html: -------------------------------------------------------------------------------- 1 | Safsata tipi belirtilmesi gereklidir. Bunlar hakkında daha fazla 2 | bilgi için safsatakilavuzu.com adresini 3 | ziyaret edebilirsiniz. -------------------------------------------------------------------------------- /web/premises/templatetags/premise_tags.py: -------------------------------------------------------------------------------- 1 | from django.template.defaulttags import register 2 | from premises.models import get_fallacy_types 3 | 4 | 5 | @register.filter 6 | def humanize_fallacy_type(value): 7 | return dict(get_fallacy_types()).get(value) 8 | -------------------------------------------------------------------------------- /web/templates/blog/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'blog/base.html' %} 2 | 3 | {% block title %}{{ post.title }}{% endblock %} 4 | 5 | {% block content %} 6 |
7 | {% include "blog/post.html" %} 8 |
9 | {% endblock %} -------------------------------------------------------------------------------- /web/templates/home.html: -------------------------------------------------------------------------------- 1 | {% if user.is_authenticated %} 2 | {% include "partials/notifications.html" with show_see_more_button=True %} 3 | {% endif %} 4 | 5 |
6 | arguman.org bir argüman analizi platformudur. 7 |
-------------------------------------------------------------------------------- /web/templates/newsfeed/fallacy.html: -------------------------------------------------------------------------------- 1 | {% load premise_tags %} 2 | 3 | bir önerme için {{ related_object.fallacy_type|humanize_fallacy_type }} bildirimi yaptı: 4 | {{ related_object.premise.text }} 5 | -------------------------------------------------------------------------------- /requirements.pip: -------------------------------------------------------------------------------- 1 | Django==1.7 2 | Unidecode==0.04.16 3 | django-social-auth==0.7.28 4 | httplib2==0.9 5 | oauth2==1.5.211 6 | python-openid==2.2.5 7 | wsgiref==0.1.2 8 | django-gravatar2==1.1.4 9 | markdown2==2.3.0 10 | pymongo==2.6.3 11 | django-markitup==2.2.2 12 | Markdown==2.5.1 13 | -------------------------------------------------------------------------------- /web/premises/utils.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | 4 | def int_or_default(value, default=None): 5 | try: 6 | return int(value) 7 | except (ValueError, TypeError): 8 | return default 9 | 10 | 11 | int_or_zero = partial(int_or_default, default=0) 12 | -------------------------------------------------------------------------------- /web/templates/newsfeed/premise.html: -------------------------------------------------------------------------------- 1 | {{ related_object.contention.title }} 2 |

3 | {{ related_object.text }} 4 |

5 | -------------------------------------------------------------------------------- /web/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "main.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /web/templates/auth/complete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 | 6 |
7 |

Registration successful

8 | 9 | Click here for login 10 | 11 |
12 | 13 | 14 | {% endblock %} -------------------------------------------------------------------------------- /web/templates/blog/index.html: -------------------------------------------------------------------------------- 1 | {% extends "blog/base.html" %} 2 | 3 | {% block content %} 4 |
5 | {% for post in posts %} 6 | {% include "blog/post.html" %} 7 | {% endfor %} 8 | {% include "blog/pagination.html" %} 9 |
10 | {% endblock %} -------------------------------------------------------------------------------- /web/premises/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | added_premise_for_contention = Signal(providing_args=["premise"]) 4 | added_premise_for_premise = Signal(providing_args=["premise"]) 5 | reported_as_fallacy = Signal(providing_args=["report"]) 6 | supported_a_premise = Signal(providing_args=["premise", "user"]) 7 | -------------------------------------------------------------------------------- /web/newsfeed/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | from newsfeed.views import NewsfeedView, PublicNewsfeedView 4 | 5 | 6 | urlpatterns = patterns('', 7 | url(r'^newsfeed$', NewsfeedView.as_view(), name='newsfeed'), 8 | url(r'^newsfeed/public$', PublicNewsfeedView.as_view(), name='public_newsfeed'), 9 | ) 10 | -------------------------------------------------------------------------------- /web/templates/notifications.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load humanize %} 4 | 5 | {% block content %} 6 |
7 |

Bildirimler

8 | {% if user.is_authenticated %} 9 | {% include "partials/notifications.html" %} 10 | {% endif %} 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /web/blog/sitemaps.py: -------------------------------------------------------------------------------- 1 | from django.contrib.sitemaps import Sitemap 2 | 3 | from blog.models import Post 4 | 5 | 6 | class BlogSitemap(Sitemap): 7 | changefreq = "never" 8 | priority = 0.9 9 | 10 | def items(self): 11 | return Post.published_objects.all() 12 | 13 | def lastmod(self, obj): 14 | return obj.date_modified 15 | -------------------------------------------------------------------------------- /web/templates/premises/actions.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | {% csrf_token %} 6 | 7 |
8 | -------------------------------------------------------------------------------- /web/templates/premises/examples/owner.html: -------------------------------------------------------------------------------- 1 | Argümanın sahibi. Bir kurum, kitap ya da kişi olabilir. 2 | Örnekler: 3 | 10 | Eğer bir değer girilmemişse argümanın sahibi argümanı ekleyen olarak görülür. -------------------------------------------------------------------------------- /web/templates/notifications/reported-as-fallacy.html: -------------------------------------------------------------------------------- 1 | {% with contention=target_object.premise.argument %} 2 | {% if contention %} 3 | {{ contention.title|truncatechars:50 }} 4 | argümanındaki önermeniz 5 | {{ target_object.get_fallacy_type_display }} olarak bildirildi. 6 | {% endif %} 7 | {% endwith %} -------------------------------------------------------------------------------- /web/templates/premises/examples/premise_type.html: -------------------------------------------------------------------------------- 1 | Önermenin tipi belirtilmesi gerekir. Değerler şunlardır: 2 | -------------------------------------------------------------------------------- /web/profiles/management/commands/update_premises.py: -------------------------------------------------------------------------------- 1 | from django.core.management import BaseCommand 2 | from premises.models import Premise 3 | 4 | 5 | class Command(BaseCommand): 6 | def handle(self, *args, **options): 7 | premises = Premise.objects.all() 8 | 9 | for premise in premises: 10 | # denormalizes sibling counts 11 | premise.save() 12 | -------------------------------------------------------------------------------- /web/premises/migrations/0009_merge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0008_auto_20141023_0133'), 11 | ('premises', '0008_auto_20141022_1951'), 12 | ] 13 | 14 | operations = [ 15 | ] 16 | -------------------------------------------------------------------------------- /web/templates/premises/examples/contention.html: -------------------------------------------------------------------------------- 1 | Önermeleriyle birlikte tartışılabilecek, desteklenebilecek, ispatlanabilecek ya da çürütülebilecek bir argüman. 2 | Örnekler: 3 | -------------------------------------------------------------------------------- /web/templates/premises/edit_contention.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |

Argüman güncelle

7 | {% csrf_token %} 8 | {{ form.as_p }} 9 | 10 |
11 |
12 | {% endblock %} -------------------------------------------------------------------------------- /web/templates/profiles/social_providers.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/templates/blog/post.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

5 |
6 |
7 | {{ post.content }} 8 |
9 |
-------------------------------------------------------------------------------- /web/templates/premises/edit_premise.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |

Önermeyi Güncelle

7 | {% csrf_token %} 8 | {{ form.as_p }} 9 | 10 |
11 |
12 | {% endblock %} -------------------------------------------------------------------------------- /web/templates/notifications/supported-a-premise.html: -------------------------------------------------------------------------------- 1 | {% with contention=target_object.argument %} 2 | {% if notification.sender and contention %} 3 | 4 | {{ notification.sender.username }} 5 | {{ contention.title|truncatechars:50 }} 6 | argümanındaki önermeni destekledi. 7 | {% endif %} 8 | {% endwith %} 9 | -------------------------------------------------------------------------------- /web/main/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for arguman project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "main.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /web/main/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | from django.contrib import admin 4 | admin.autodiscover() 5 | 6 | urlpatterns = patterns('', 7 | url(r'^', include('newsfeed.urls')), 8 | url(r'^', include('premises.urls')), 9 | url(r'^', include('profiles.urls')), 10 | url(r'^blog/', include('blog.urls')), 11 | url(r'^', include('social_auth.urls')), 12 | url(r'^admin/', include(admin.site.urls)), 13 | ) 14 | -------------------------------------------------------------------------------- /web/premises/migrations/0005_auto_20141012_0301.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0004_auto_20141012_0056'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='contention', 16 | options={'ordering': ['-date_creation']}, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /web/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 | {% include "profiles/social_providers.html" %} 7 |

Kullanıcı adınla giriş yap

8 | {% csrf_token %} 9 | {{ form.as_p }} 10 | 11 |
12 | 13 |
14 | {% endblock %} -------------------------------------------------------------------------------- /web/templates/premises/report.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 | 7 |
8 |

{{ premise.text }}

9 |
10 | {% csrf_token %} 11 | {{ form.as_p }} 12 | 13 |
14 |
15 | {% endblock %} -------------------------------------------------------------------------------- /web/blog/migrations/0002_post_is_announcement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('blog', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='post', 16 | name='is_announcement', 17 | field=models.BooleanField(default=False), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /web/premises/migrations/0003_premise_text.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0002_auto_20141009_1922'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='premise', 16 | name='text', 17 | field=models.TextField(null=True, blank=True), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /web/premises/migrations/0013_premise_child_count.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0012_auto_20141027_1905'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='premise', 16 | name='child_count', 17 | field=models.IntegerField(default=1), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /web/premises/migrations/0002_auto_20141009_1922.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='premise', 16 | name='parent', 17 | field=models.ForeignKey(related_name=b'children', blank=True, to='premises.Premise', null=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /web/premises/migrations/0015_premise_collapsed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0014_premise_max_sibling_count'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='premise', 16 | name='collapsed', 17 | field=models.BooleanField(default=False), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /web/premises/migrations/0006_contention_is_published.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0005_auto_20141012_0301'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='contention', 16 | name='is_published', 17 | field=models.BooleanField(default=False), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /web/premises/migrations/0014_premise_max_sibling_count.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0013_premise_child_count'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='premise', 16 | name='max_sibling_count', 17 | field=models.IntegerField(default=1), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /web/templates/blog/latest.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 | 15 |
16 |
-------------------------------------------------------------------------------- /web/templates/notifications/added-premise-for-contention.html: -------------------------------------------------------------------------------- 1 | {% with contention=target_object.argument %} 2 | {% if target_object.user and contention %} 3 | 4 | {{ target_object.user.username }} 5 | başlattığınız argüman {{ contention.title|truncatechars:50 }} 6 | için bir 7 | {{ target_object.get_premise_type_display }} 8 | önermesi gönderdi. 9 | {% endif %} 10 | {% endwith %} 11 | -------------------------------------------------------------------------------- /web/templates/notifications/added-premise-for-premise.html: -------------------------------------------------------------------------------- 1 | {% with contention=target_object.argument %} 2 | {% if target_object.user and contention %} 3 | 4 | {{ target_object.user.username }} 5 | {{ contention.title|truncatechars:50 }} 6 | argümanındaki önermeniz için başka bir 7 | {{ target_object.get_premise_type_display }} 8 | önermesi gönderdi. 9 | {% endif %} 10 | {% endwith %} 11 | -------------------------------------------------------------------------------- /web/templates/auth/update.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |

Bilgilerim

7 | {% csrf_token %} 8 | {{ form.as_p }} 9 |

10 | Avatar yüklemek için gravatar.com 11 | adresini kullanabilirsiniz. 12 |

13 | 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /web/premises/migrations/0019_premise_date_creation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import datetime 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('premises', '0018_premise_supporters'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='premise', 17 | name='date_creation', 18 | field=models.DateTimeField(default=datetime.date(2014, 11, 5), auto_now_add=True), 19 | preserve_default=False, 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /web/templates/premises/new_contention.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 | {% csrf_token %} 7 | {{ form.as_p }} 8 |

9 | Bu argümanı göndererek kullanım koşullarını kabul etmiş olacaksınız ve uygun görülmeyen 10 | içerikler moderatörler tarafından yayından kaldırılacaktır. 11 |

12 | 13 |
14 |
15 | {% endblock %} -------------------------------------------------------------------------------- /web/newsfeed/management/commands/create_initial_newsfeed.py: -------------------------------------------------------------------------------- 1 | from django.core.management import BaseCommand 2 | from newsfeed.models import Entry 3 | from premises.models import Contention 4 | 5 | 6 | class Command(BaseCommand): 7 | 8 | def handle(self, *args, **options): 9 | for contention in Contention.objects.all(): 10 | Entry.objects.create( 11 | object_id=contention.id, 12 | news_type=contention.get_newsfeed_type(), 13 | sender=contention.get_actor(), 14 | related_object=contention.get_newsfeed_bundle(), 15 | date_creation=contention.date_creation 16 | ) 17 | -------------------------------------------------------------------------------- /web/templates/premises/contention.html: -------------------------------------------------------------------------------- 1 | {% for contention in contentions %} 2 |
  • 3 |
    4 | {{ contention.because.count }} çünkü 5 | {{ contention.but.count }} ama 6 | {{ contention.however.count }} ancak 7 |
    8 |
    9 | {{ contention.title }} 10 | 11 |
    12 |
  • 13 | {% endfor %} 14 | -------------------------------------------------------------------------------- /web/premises/migrations/0018_premise_supporters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('premises', '0017_auto_20141030_0353'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='premise', 18 | name='supporters', 19 | field=models.ManyToManyField(related_name=b'supporting', to=settings.AUTH_USER_MODEL), 20 | preserve_default=True, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /web/premises/migrations/0012_auto_20141027_1905.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0011_auto_20141027_1845'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='premise', 16 | name='deleted_at', 17 | field=models.DateTimeField(null=True, blank=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AddField( 21 | model_name='premise', 22 | name='is_deleted', 23 | field=models.BooleanField(default=False), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /web/templates/auth/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
    5 |
    6 | {% include "profiles/social_providers.html" %} 7 |

    Kayıt ol

    8 | {% csrf_token %} 9 | {{ form.as_p }} 10 | 15 | 16 |
    17 |
    18 | {% endblock %} -------------------------------------------------------------------------------- /web/premises/migrations/0011_auto_20141027_1845.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0010_auto_20141027_0204'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='contention', 16 | name='deleted_at', 17 | field=models.DateTimeField(null=True, blank=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AddField( 21 | model_name='contention', 22 | name='is_deleted', 23 | field=models.BooleanField(default=False), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /web/blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | from blog.sitemaps import BlogSitemap 4 | from blog.views import BlogDetailView, BlogIndexView, \ 5 | BlogPostsRssFeed, BlogPostsAtomFeed 6 | 7 | 8 | urlpatterns = patterns('', 9 | 10 | # blog urls 11 | url(r'^$', BlogIndexView.as_view(), name="blog"), 12 | url(r'^(?P[-\w]+)/$', BlogDetailView.as_view(), name="blog_detail"), 13 | 14 | # rss & atom feed 15 | url(r'^feed/rss$', BlogPostsRssFeed(), name="blog_rss_feed"), 16 | url(r'^feed/atom', BlogPostsAtomFeed(), name="blog_atom_feed"), 17 | 18 | # sitemap 19 | url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', 20 | {'sitemaps': { 21 | "blog": BlogSitemap() 22 | }}, name="blog_sitemap"), 23 | 24 | ) 25 | -------------------------------------------------------------------------------- /web/premises/migrations/0020_auto_20141105_0153.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0019_premise_date_creation'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='contention', 16 | name='ip_address', 17 | field=models.IPAddressField(null=True, blank=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AddField( 21 | model_name='premise', 22 | name='ip_address', 23 | field=models.IPAddressField(null=True, blank=True), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /web/blog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | from blog.models import Post 5 | 6 | 7 | class PostAdmin(admin.ModelAdmin): 8 | list_display = ["title", "slug", "is_published"] 9 | list_filter = ["is_published"] 10 | search_fields = ["title", "slug", "content"] 11 | actions = ["publish", "mark_as_draft"] 12 | prepopulated_fields = { 13 | "slug": ("title", ) 14 | } 15 | 16 | def publish(self, request, qs): 17 | qs.update(is_published=True) 18 | publish.short_description = _("Publish selected posts") 19 | 20 | def mark_as_draft(self, request, qs): 21 | qs.update(is_published=False) 22 | mark_as_draft.short_description = _("Mark as draft selected posts") 23 | 24 | 25 | admin.site.register(Post, PostAdmin) 26 | -------------------------------------------------------------------------------- /web/profiles/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin 3 | from django.contrib.auth.models import User 4 | 5 | from django_gravatar.templatetags.gravatar import gravatar as gravatar_for_user 6 | 7 | from profiles.models import Profile, Notification 8 | 9 | 10 | class ProfileAdmin(UserAdmin): 11 | 12 | list_display = ('gravatar', 'username', 'email', 'first_name', 13 | 'last_name', 'is_staff') 14 | ordering = ("-id", ) 15 | 16 | def gravatar(self, obj): 17 | return gravatar_for_user(obj) 18 | gravatar.allow_tags = True 19 | 20 | 21 | class NotificationAdmin(admin.ModelAdmin): 22 | list_display = ('sender', 'recipient', 'notification_type') 23 | 24 | 25 | admin.site.register(Profile, ProfileAdmin) 26 | admin.site.register(Notification, NotificationAdmin) 27 | -------------------------------------------------------------------------------- /web/templates/partials/notifications.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | {% if user.notifications.exists %} 3 |
    4 | {% if notifications %} 5 | 16 | {% else %} 17 |
    18 | Hiç yeni bildiriminiz yok. 19 | {% if show_see_more_button %} 20 | Eskilerini görüntüle 21 | {% endif %} 22 |
    23 | {% endif %} 24 |
    25 | {% endif %} -------------------------------------------------------------------------------- /web/templates/infinity-pagination.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/profiles/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from django.views.generic import TemplateView 3 | from profiles.views import (RegistrationView, LoginView, LogoutView, 4 | ProfileDetailView, ProfileUpdateView) 5 | 6 | 7 | urlpatterns = patterns('', 8 | url(r'^login/$', LoginView.as_view(template_name="auth/login.html"), name='auth_login'), 9 | url(r'^logout/$', LogoutView.as_view(), name='auth_logout'), 10 | url(r'^auth/profile$', ProfileUpdateView.as_view(), name='auth_profile_update'), 11 | url(r'^register/$', RegistrationView.as_view( 12 | template_name="auth/register.html"), name='auth_registration'), 13 | url(r'^complete/$', TemplateView.as_view( 14 | template_name="auth/complete.html"), name='auth_registration_complete'), 15 | url(r'^users/(?P[\w\._-]+)$', ProfileDetailView.as_view( 16 | template_name="auth/profile.html"), name='auth_profile'), 17 | 18 | ) -------------------------------------------------------------------------------- /web/newsfeed/utils.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.utils.functional import SimpleLazyObject 3 | 4 | from pymongo import Connection 5 | 6 | _connection = None 7 | 8 | 9 | def get_connection(): 10 | global _connection 11 | if not _connection: 12 | _connection = Connection( 13 | host=getattr(settings, 'MONGODB_HOST', None), 14 | port=getattr(settings, 'MONGODB_PORT', None) 15 | ) 16 | username = getattr(settings, 'MONGODB_USERNAME', None) 17 | password = getattr(settings, 'MONGODB_PASSWORD', None) 18 | db = _connection[settings.MONGODB_DATABASE] 19 | if username and password: 20 | db.authenticate(username, password) 21 | return db 22 | return _connection[settings.MONGODB_DATABASE] 23 | 24 | 25 | connection = SimpleLazyObject(get_connection) 26 | 27 | 28 | def get_collection(collection_name): 29 | return getattr(connection, collection_name) 30 | -------------------------------------------------------------------------------- /web/premises/migrations/0021_auto_20141107_0059.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0020_auto_20141105_0153'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Channel', 16 | fields=[ 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 18 | ('title', models.CharField(max_length=255)), 19 | ], 20 | options={ 21 | }, 22 | bases=(models.Model,), 23 | ), 24 | migrations.AddField( 25 | model_name='contention', 26 | name='channel', 27 | field=models.ForeignKey(related_name=b'contentions', blank=True, to='premises.Channel', null=True), 28 | preserve_default=True, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /web/templates/tabs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/premises/migrations/0004_auto_20141012_0056.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import datetime 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('premises', '0003_premise_text'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='contention', 17 | name='date_creation', 18 | field=models.DateTimeField(default=datetime.datetime(2014, 10, 12, 0, 56, 45, 227936), auto_now_add=True), 19 | preserve_default=False, 20 | ), 21 | migrations.AlterField( 22 | model_name='contention', 23 | name='slug', 24 | field=models.SlugField(max_length=255, blank=True), 25 | ), 26 | migrations.AlterField( 27 | model_name='premise', 28 | name='argument', 29 | field=models.ForeignKey(related_name=b'premises', to='premises.Contention'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /web/premises/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.db.models import Count 3 | 4 | from premises.models import Contention, Premise, Comment, Report, Channel 5 | 6 | 7 | class ReportAdmin(admin.ModelAdmin): 8 | list_display = ('reporter', 'premise', 'contention') 9 | 10 | 11 | class ContentionAdmin(admin.ModelAdmin): 12 | list_display = ('title', 'channel') 13 | list_editable = ('channel', ) 14 | list_per_page = 10 15 | 16 | def get_queryset(self, request): 17 | return Contention.objects.annotate( 18 | report_count=Count("reports") 19 | ).filter( 20 | report_count__gt=0 21 | ) 22 | 23 | 24 | class PremiseAdmin(admin.ModelAdmin): 25 | list_display = ('text', 'argument', 'is_deleted') 26 | list_filter = ('is_deleted',) 27 | 28 | def get_queryset(self, request): 29 | return Premise.objects.all_with_deleted() 30 | 31 | admin.site.register(Report, ReportAdmin) 32 | admin.site.register(Contention, ContentionAdmin) 33 | admin.site.register(Premise, PremiseAdmin) 34 | admin.site.register(Comment) 35 | admin.site.register(Channel) 36 | -------------------------------------------------------------------------------- /web/premises/managers.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.db.models import query 3 | from datetime import datetime 4 | 5 | from premises.constants import FEATURED_CONTENT_COUNT 6 | 7 | 8 | class DeletePreventionQueryset(query.QuerySet): 9 | def delete(self): 10 | return super(DeletePreventionQueryset, self).update( 11 | is_deleted=True, deleted_at=datetime.now()) 12 | 13 | def hard_delete(self): 14 | return super(DeletePreventionQueryset, self).delete() 15 | 16 | 17 | class DeletePreventionManager(models.Manager): 18 | 19 | def get_queryset(self): 20 | queryset = DeletePreventionQueryset(self.model, using=self._db) 21 | return queryset.filter(is_deleted=False) 22 | 23 | def deleted_set(self): 24 | queryset = DeletePreventionQueryset(self.model, using=self._db) 25 | return queryset.filter(is_deleted=True) 26 | 27 | def all_with_deleted(self): 28 | return DeletePreventionQueryset(self.model, using=self._db) 29 | 30 | 31 | class ContentionManager(DeletePreventionManager): 32 | def featured(self): 33 | return self.filter(is_featured=True) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Fatih Erikli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /web/profiles/mixins.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.contrib.auth.decorators import login_required 4 | from django.http import HttpResponse 5 | from django.utils.decorators import method_decorator 6 | 7 | 8 | class LoginRequiredMixin(object): 9 | """ 10 | Login required mixin. 11 | """ 12 | @method_decorator(login_required) 13 | def dispatch(self, *args, **kwargs): 14 | return super(LoginRequiredMixin, self).dispatch(*args, **kwargs) 15 | 16 | 17 | class JSONResponseMixin(object): 18 | """ 19 | A mixin that can be used to render a JSON response. 20 | """ 21 | response_class = HttpResponse 22 | 23 | def render_to_response(self, context, **response_kwargs): 24 | """ 25 | Returns a JSON response, transforming 'context' to make the payload. 26 | """ 27 | response_kwargs['content_type'] = 'application/json' 28 | return self.response_class( 29 | self.convert_context_to_json(context), 30 | **response_kwargs 31 | ) 32 | 33 | def convert_context_to_json(self, context): 34 | """ 35 | Convert the context dictionary into a JSON object 36 | """ 37 | return json.dumps(context) 38 | -------------------------------------------------------------------------------- /web/premises/mixins.py: -------------------------------------------------------------------------------- 1 | from django.db.models import signals 2 | from django.db import models 3 | from datetime import datetime 4 | 5 | 6 | class FormRenderer(object): 7 | def as_p(self): 8 | "Returns this form rendered as HTML

    s." 9 | return self._html_output( 10 | normal_row='%(label)s %(field)s%(help_text)s

    ', 11 | error_row='%s', 12 | row_ender='

    ', 13 | help_text_html='
    %s
    ', 14 | errors_on_separate_row=True) 15 | 16 | 17 | class DeletePreventionMixin(models.Model): 18 | 19 | is_deleted = models.BooleanField(default=False) 20 | deleted_at = models.DateTimeField(null=True, blank=True) 21 | 22 | class Meta: 23 | abstract = True 24 | 25 | def delete(self, using=None): 26 | # prepare 27 | signals.pre_delete.send( 28 | sender=self.__class__, 29 | instance=self 30 | ) 31 | # mark as deleted 32 | self.is_deleted = True 33 | self.deleted_at = datetime.now() 34 | self.save(using=using) 35 | # trigger 36 | signals.post_delete.send( 37 | sender=self.__class__, 38 | instance=self 39 | ) 40 | -------------------------------------------------------------------------------- /web/premises/fallacies.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["Begging The Question,", "Kısır Döngü Safsatası"], 3 | ["Irrelevant Conclusion", "Alakasız Sonuç Safsatası"], 4 | ["Fallacy of Irrelevant Purpose", "Alakasız Amaç Safsatası"], 5 | ["Fallacy of Red Herring", "Konuyu Saptırma Safsatası"], 6 | ["Argument Against the Man", "Adam Karalama Safsatası"], 7 | ["Poisoning The Well", "Dolduruşa Getirme Safsatası"], 8 | ["Fallacy Of The Beard", "Devede Kulak Safsatası"], 9 | ["Fallacy of Slippery Slope", "Felaket Tellallığı Safsatası"], 10 | ["Fallacy of False Cause", "Yanlış Sebep Safsatası"], 11 | ["Fallacy of “Previous This”", "Öncesinde Safsatası"], 12 | ["Joint Effect", "Müşterek Etki"], 13 | ["Wrong Direction", "Yanlış Yön Safsatası"], 14 | ["False Analogy", "Yanlış Benzetme Safsatası"], 15 | ["Slothful Induction", "Yok Sayma Safsatası"], 16 | ["Appeal to Belief", "İnanca Başvurma Safsatası"], 17 | ["Pragmatic Fallacy", "Faydacı Safsata"], 18 | ["Fallacy Of “İs” To “Ought”", "Dayatma Safsatası"], 19 | ["Argument From Force", "Tehdit Safsatası"], 20 | ["Argument To Pity", "Duygu Sömürüsü"], 21 | ["Prejudicial Language", "Önyargılı Dil Safsatası"], 22 | ["Fallacy Of Special Pleading", "Mazeret Safsatası"], 23 | ["Appeal To Authority", "Bir Bilen Safsatası"] 24 | ] 25 | -------------------------------------------------------------------------------- /web/premises/migrations/0008_auto_20141023_0133.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import datetime 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('premises', '0007_auto_20141020_2215'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='contention', 17 | name='date_modification', 18 | field=models.DateTimeField(default=datetime.datetime(2014, 10, 23, 1, 33, 8, 359184), auto_now=True, auto_now_add=True), 19 | preserve_default=False, 20 | ), 21 | migrations.AlterField( 22 | model_name='premise', 23 | name='is_approved', 24 | field=models.BooleanField(default=False, verbose_name=b'Yay\xc4\xb1nla'), 25 | ), 26 | migrations.AlterField( 27 | model_name='premise', 28 | name='parent', 29 | field=models.ForeignKey(related_name=b'children', blank=True, to='premises.Premise', help_text=b'\xc3\x96nermenin \xc3\xb6nc\xc3\xbcl\xc3\xbc. E\xc4\x9fer bo\xc5\x9f b\xc4\xb1rak\xc4\xb1l\xc4\xb1rsaana arg\xc3\xbcman\xc4\xb1n bir \xc3\xb6nermesi olur.', null=True, verbose_name=b'\xc3\x96nc\xc3\xbcl\xc3\xbc'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /web/blog/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import markitup.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Post', 16 | fields=[ 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 18 | ('title', models.CharField(max_length=255, verbose_name='Name')), 19 | ('slug', models.SlugField(unique=True, max_length=255, verbose_name='Slug')), 20 | ('content', markitup.fields.MarkupField(no_rendered_field=True, verbose_name='Content')), 21 | ('date_created', models.DateTimeField(auto_now_add=True)), 22 | ('date_modified', models.DateTimeField(auto_now=True, auto_now_add=True)), 23 | ('is_published', models.BooleanField(default=True, verbose_name='Published')), 24 | ('_content_rendered', models.TextField(editable=False, blank=True)), 25 | ], 26 | options={ 27 | 'ordering': ('-date_created',), 28 | }, 29 | bases=(models.Model,), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /web/blog/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils.encoding import smart_unicode 3 | from django.utils.translation import ugettext_lazy as _ 4 | 5 | from markitup.fields import MarkupField 6 | 7 | 8 | class PublishedManager(models.Manager): 9 | """ 10 | Returns published blog posts. 11 | """ 12 | def get_queryset(self): 13 | queryset = super(PublishedManager, self).get_queryset() 14 | return queryset.filter(is_published=True) 15 | 16 | 17 | class Post(models.Model): 18 | """ 19 | Holds blog post data. 20 | """ 21 | title = models.CharField(_("Name"), max_length=255) 22 | slug = models.SlugField(_("Slug"), max_length=255, unique=True) 23 | content = MarkupField(_("Content")) 24 | date_created = models.DateTimeField(auto_now_add=True) 25 | date_modified = models.DateTimeField(auto_now=True, auto_now_add=True) 26 | is_published = models.BooleanField(_("Published"), default=True) 27 | is_announcement = models.BooleanField(default=False) 28 | 29 | objects = models.Manager() 30 | published_objects = PublishedManager() 31 | 32 | class Meta: 33 | ordering = ("-date_created", ) 34 | 35 | def __unicode__(self): 36 | return smart_unicode(self.title) 37 | 38 | @models.permalink 39 | def get_absolute_url(self): 40 | return "blog_detail", [self.slug] 41 | -------------------------------------------------------------------------------- /web/profiles/migrations/0002_notification.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Notification', 17 | fields=[ 18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 19 | ('date_created', models.DateTimeField(auto_now_add=True)), 20 | ('notification_type', models.IntegerField(choices=[(0, b'added-premise-for-contention'), (1, b'added-premise-for-premise'), (2, b'reported-as-fallacy'), (3, b'followed')])), 21 | ('is_read', models.BooleanField(default=False)), 22 | ('target_object_id', models.IntegerField(null=True, blank=True)), 23 | ('recipient', models.ForeignKey(related_name=b'notifications', to=settings.AUTH_USER_MODEL)), 24 | ('sender', models.ForeignKey(related_name=b'sent_notifications', blank=True, to=settings.AUTH_USER_MODEL, null=True)), 25 | ], 26 | options={ 27 | 'ordering': ['is_read', '-date_created'], 28 | }, 29 | bases=(models.Model,), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /web/blog/views.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.contrib.syndication.views import Feed 3 | from django.utils.feedgenerator import Atom1Feed 4 | from django.views.generic import ListView 5 | from django.views.generic.detail import DetailView 6 | 7 | from blog.models import Post 8 | 9 | 10 | class BlogIndexView(ListView): 11 | template_name = "blog/index.html" 12 | queryset = Post.published_objects.all() 13 | context_object_name = "posts" 14 | paginate_by = 30 15 | 16 | 17 | class BlogDetailView(DetailView): 18 | template_name = "blog/detail.html" 19 | model = Post 20 | context_object_name = "post" 21 | 22 | def get_queryset(self): 23 | if self.request.user.is_superuser: 24 | return self.model.objects.all() 25 | return self.model.published_objects.all() 26 | 27 | 28 | class BlogPostsRssFeed(Feed): 29 | title = settings.BLOG_FEED_TITLE 30 | link = settings.BLOG_URL 31 | description = settings.BLOG_FEED_DESCRIPTION 32 | 33 | def items(self): 34 | return Post.objects.all()[:20] 35 | 36 | def item_description(self, post): 37 | return post.content 38 | 39 | def item_pubdate(self, post): 40 | return post.date_created 41 | 42 | def item_categories(self, post): 43 | return [tag.name for tag in post.tags.all()] 44 | 45 | 46 | class BlogPostsAtomFeed(BlogPostsRssFeed): 47 | feed_type = Atom1Feed 48 | subtitle = settings.BLOG_FEED_DESCRIPTION 49 | -------------------------------------------------------------------------------- /web/premises/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from premises.constants import MAX_PREMISE_CONTENT_LENGTH 3 | 4 | from premises.mixins import FormRenderer 5 | from premises.models import Contention, Premise, Report 6 | 7 | 8 | class ArgumentCreationForm(FormRenderer, forms.ModelForm): 9 | class Meta: 10 | model = Contention 11 | fields = ['title', 'owner', 'sources'] 12 | 13 | 14 | class PremiseCreationForm(FormRenderer, forms.ModelForm): 15 | 16 | class Meta: 17 | model = Premise 18 | fields = ['premise_type', 'text', 'sources'] 19 | widgets = { 20 | 'premise_type': forms.RadioSelect, 21 | 'text': forms.Textarea(attrs={ 22 | 'maxlength': MAX_PREMISE_CONTENT_LENGTH 23 | }) 24 | } 25 | 26 | 27 | class PremiseEditForm(FormRenderer, forms.ModelForm): 28 | class Meta: 29 | model = Premise 30 | fields = ['premise_type', 'text', 'sources', 'parent', 'is_approved'] 31 | widgets = { 32 | 'premise_type': forms.RadioSelect 33 | } 34 | 35 | def __init__(self, *args, **kwargs): 36 | super(PremiseEditForm, self).__init__(*args, **kwargs) 37 | queryset = (self.instance 38 | .argument 39 | .premises 40 | .exclude(pk=self.instance.pk)) # avoid self-loop 41 | self.fields['parent'].queryset = queryset 42 | 43 | 44 | class ReportForm(forms.ModelForm): 45 | class Meta: 46 | model = Report 47 | fields = ["fallacy_type"] 48 | -------------------------------------------------------------------------------- /web/templates/premises/new_premise.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
    5 | {% ifnotequal contention.user user %} 6 |
    7 | Göndereceğiniz önerme direkt olarak yayına alınacaktır, 8 | ancak yayında kalacağının garantisi yoktur. Kullanım Koşullarına uygun görülmeyen 9 | içerikler moderatörler tarafından yayından kaldırılacaktır. 10 |
    11 | {% endifnotequal %} 12 |
    13 | {% if parent %} 14 |
    15 | Ekleyeceğiniz önermenin öncülü: 16 |

    {{ parent.text }}

    17 |
    18 | {% endif %} 19 | {% csrf_token %} 20 | {{ form.as_p }} 21 | 22 |
    23 |
    24 | {% endblock %} 25 | 26 | {% block extra-scripts %} 27 | 44 | {% endblock %} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Argüman.org](http://arguman.org/static/img/logo.png) 2 | ---- 3 | [Arguman.org](http://arguman.org) bir arguman analiz & [haritalama](https://en.wikipedia.org/wiki/Argument_map) platformudur. 4 | 5 | ## Nasıl çalışır 6 | 7 | Kullanıcılar önermeleriyle birlikte tartışılabilecek, desteklenebilecek, ispatlanabilecek ya da çürütülebilecek bir argüman [öne sürerler](http://arguman.org/new-argument) ve bunu argümanına *ÇÜNKÜ, AMA, ANCAK* ve benzeri önerilerle haklılığını/doğruluğunu savunur. 8 | 9 | ![](http://i.imgur.com/yEjemt1.png) 10 | 11 | ## Faydası 12 | 13 | Eleştirel düşünme, bir bir bilginin tarafsız, gerçekçi ve etkin bir şekilde değerlendirilmesi ile alakalı beceriler bütünüdür. Günlük hayatımızda her ne kadar inkar etsek de doğamız gereği öznel yargılar yapabiliyoruz. Eleştirel düşünmede amaç bir veriyi değerlendirirken bu öznel/taraflı yargılardan kurtulabilmektir. 14 | 15 | Eleştirel düşünmenin temeli argümanlardır. Değerlendirme bir argüman ve bu argümanın önermeleri, destekleyicileri, itirazları, çürütmeleri üzerinden yapılır. Argüman haritaları ise bu öğelerin görselleştirilmiş halidir. 16 | 17 | Argüman haritalarını görsel olarak hiyerarşi haritaları olarak düşünebilirsiniz. 18 | 19 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/9/99/Whatley.png/800px-Whatley.png) 20 | 21 | **Argüman.org'un amacı ise argümanların birden çok kullanıcı tarafından tarafsız bir şekilde haritalanabilmesidir.** 22 | 23 | ## Katkıda bulunanlar 24 | - Fatih Erikli 25 | - Tuna Vargı 26 | - Huseyin Mert 27 | - Erdem Ozkol 28 | - Aybars Badur 29 | - Can Göktaş 30 | - Bahattin Çiniç 31 | - Murat Çorlu 32 | - Cumhur Korkut 33 | - Serdar Dalgıç 34 | - Emir Karşıyakalı 35 | 36 | ## Ayrıca teşekkürler 37 | - Burak Arıkan 38 | - Kadir Akkara Yardımcı 39 | 40 | Öneri ve şikayet için [bize yazabilirsiniz](https://github.com/arguman/arguman.org/issues). 41 | 42 | 43 | -------------------------------------------------------------------------------- /web/premises/migrations/0008_auto_20141022_1951.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('premises', '0007_auto_20141020_2215'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Report', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('contention', models.ForeignKey(related_name=b'contention_report', blank=True, to='premises.Contention', null=True)), 21 | ('premise', models.ForeignKey(related_name=b'comment_report', blank=True, to='premises.Premise', null=True)), 22 | ('reporter', models.ForeignKey(related_name=b'reporter', to=settings.AUTH_USER_MODEL)), 23 | ('user', models.ForeignKey(related_name=b'user_report', blank=True, to=settings.AUTH_USER_MODEL, null=True)), 24 | ], 25 | options={ 26 | }, 27 | bases=(models.Model,), 28 | ), 29 | migrations.AlterField( 30 | model_name='premise', 31 | name='is_approved', 32 | field=models.BooleanField(default=False, verbose_name=b'Yay\xc4\xb1nla'), 33 | ), 34 | migrations.AlterField( 35 | model_name='premise', 36 | name='parent', 37 | field=models.ForeignKey(related_name=b'children', blank=True, to='premises.Premise', help_text=b'\xc3\x96nermenin \xc3\xb6nc\xc3\xbcl\xc3\xbc. E\xc4\x9fer bo\xc5\x9f b\xc4\xb1rak\xc4\xb1l\xc4\xb1rsaana arg\xc3\xbcman\xc4\xb1n bir \xc3\xb6nermesi olur.', null=True, verbose_name=b'\xc3\x96nc\xc3\xbcl\xc3\xbc'), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /web/templates/newsfeed.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load humanize %} 4 | {% load gravatar %} 5 | 6 | {% block content %} 7 |
    8 | {% include "home.html" %} 9 | {% include "tabs.html" %} 10 | {% if user.is_anonymous %} 11 |

    neler oluyor burada:

    12 | {% else %} 13 |
    14 | {% if newsfeed_is_public %} 15 | Sadece takip ettiklerini görüntüle 16 | {% else %} 17 | Herkesin haberlerini görüntüle 18 | {% endif %} 19 |
    20 | {% endif %} 21 |
    22 | {% for entry in news_entries %} 23 |
    24 |
    25 | {% gravatar entry.sender.email %} 26 | {{ entry.sender.username|lower }} 27 |
    28 |
    29 | {{ entry.render }} 30 | 31 |
    32 |
    33 | {% empty %} 34 |

    Haber kaynağınız boş. Birilerini takip edebilirsiniz.

    35 | {% endfor %} 36 | {% if has_next_page %} 37 |
    38 | 39 |
    40 | {% endif %} 41 |
    42 |
    43 | {% endblock %} 44 | 45 | {% block extra-scripts %} 46 | {% include "infinity-pagination.html" %} 47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /web/templates/about.md: -------------------------------------------------------------------------------- 1 | ## Eleştirel düşünme 2 | 3 | Eleştirel düşünme, bir bir bilginin tarafsız, gerçekçi ve etkin bir şekilde 4 | değerlendirilmesi ile alakalı beceriler bütünüdür. Günlük hayatımızda 5 | her ne kadar inkar etsek de doğamız gereği öznel yargılar yapabiliyoruz. Eleştirel 6 | düşünmede amaç bir veriyi değerlendirirken bu öznel/taraflı yargılardan kurtulabilmektir. 7 | 8 | ## Argüman haritaları 9 | 10 | Eleştirel düşünmenin temeli argümanlardır. Değerlendirme bir argüman ve 11 | bu argümanın önermeleri, destekleyicileri, itirazları, çürütmeleri üzerinden yapılır. 12 | Argüman haritaları ise bu öğelerin görselleştirilmiş halidir. 13 | 14 | Argüman haritalarını görsel olarak hiyerarşi haritaları olarak düşünebilirsiniz. 15 | 16 | 17 | 18 | Temel hedef argümanın tüm öğelerini tarafsız ve safsatadan uzak bir şekilde 19 | resmedebilmektir. 20 | 21 | ## Argüman analizi platformu 22 | 23 | Argüman.org'un amacı ise argümanların birden çok kullanıcı tarafından 24 | tarafsız bir şekilde haritalanabilmesidir. 25 | 26 | 27 | ## Biz kimiz 28 | 29 | Arguman.org açık kaynak bir proje olup topluluk tarafından geliştirilmektedir. 30 | 31 | 32 | 33 | Eğer siz de bir şeyler yapmak isterseniz yukarıdaki github adresini inceleyebilirsiniz. 34 | 35 | ## Katkıda bulunanlar ve yaptıkları 36 | 37 | - [Fatih Erikli](http://fatiherikli.com) (kurucu, geliştirme) 38 | - [Hüseyin Mert](http://hmert.com) (proje yönetimi) 39 | - [Aybars Badur](https://twitter.com/aybarsbadur) (geliştirme) 40 | - [Tuna Vargı](http://tunavargi.com/) (geliştirme) 41 | - [Bahattin Çiniç](http://bahattincinic.com/) (geliştirme) 42 | - [Can Göktaş](https://twitter.com/cangokt) (arayüz geliştirme) 43 | - [Kağan Yaldızkaya](https://dribbble.com/kagan) (yeni tasarım üzerinde çalışmakta) 44 | - [Mehmet İnce](https://twitter.com/mdisec) (güvenlik testi) 45 | 46 | İletişim için argumananalizi@gmail.com adresine yazabilirsiniz. 47 | -------------------------------------------------------------------------------- /web/premises/migrations/0017_auto_20141030_0353.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0016_report_fallacy_type'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='report', 16 | name='fallacy_type', 17 | field=models.CharField(default=b'Wrong Direction', choices=[['Begging The Question,', 'K\u0131s\u0131r D\xf6ng\xfc Safsatas\u0131'], ['Irrelevant Conclusion', 'Alakas\u0131z Sonu\xe7 Safsatas\u0131'], ['Fallacy of Irrelevant Purpose', 'Alakas\u0131z Ama\xe7 Safsatas\u0131'], ['Fallacy of Red Herring', 'Konuyu Sapt\u0131rma Safsatas\u0131'], ['Argument Against the Man', 'Adam Karalama Safsatas\u0131'], ['Poisoning The Well', 'Dolduru\u015fa Getirme Safsatas\u0131'], ['Fallacy Of The Beard', 'Devede Kulak Safsatas\u0131'], ['Fallacy of Slippery Slope', 'Felaket Tellall\u0131\u011f\u0131 Safsatas\u0131'], ['Fallacy of False Cause', 'Yanl\u0131\u015f Sebep Safsatas\u0131'], ['Fallacy of \u201cPrevious This\u201d', '\xd6ncesinde Safsatas\u0131'], ['Joint Effect', 'M\xfc\u015fterek Etki'], ['Wrong Direction', 'Yanl\u0131\u015f Y\xf6n Safsatas\u0131'], ['False Analogy', 'Yanl\u0131\u015f Benzetme Safsatas\u0131'], ['Slothful Induction', 'Yok Sayma Safsatas\u0131'], ['Appeal to Belief', '\u0130nanca Ba\u015fvurma Safsatas\u0131'], ['Pragmatic Fallacy', 'Faydac\u0131 Safsata'], ['Fallacy Of \u201c\u0130s\u201d To \u201cOught\u201d', 'Dayatma Safsatas\u0131'], ['Argument From Force', 'Tehdit Safsatas\u0131'], ['Argument To Pity', 'Duygu S\xf6m\xfcr\xfcs\xfc'], ['Prejudicial Language', '\xd6nyarg\u0131l\u0131 Dil Safsatas\u0131'], ['Fallacy Of Special Pleading', 'Mazeret Safsatas\u0131']], max_length=255, help_text='Safsata tipi belirtilmesi gereklidir. Bunlar hakk\u0131nda daha fazla\nbilgi i\xe7in safsatakilavuzu.com adresini\nziyaret edebilirsiniz.', null=True, verbose_name=b'Safsata Tipi'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /web/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load humanize %} 4 | 5 | {% block content %} 6 |
    7 | 8 | {% include "home.html" %} 9 | {% include "tabs.html" %} 10 | 11 | {% ifequal tab_class 'search' %} 12 |
    13 | 14 | 15 |
    16 | {% endifequal %} 17 |
      18 | {% for contention in contentions %} 19 |
    • 20 |
      21 | {{ contention.because.count }} çünkü 22 | {{ contention.but.count }} ama 23 | {{ contention.however.count }} ancak 24 |
      25 |
      26 | {{ contention.title }} 27 | 28 |
      son gönderen: {{ contention.last_user }} 30 | 31 |
      32 |
      33 |
    • 34 | {% empty %} 35 | {% if keywords %} 36 |
    • Sonuç bulunamadı.
    • 37 | {% endif %} 38 | {% endfor %} 39 | {% if has_next_page %} 40 |
    • 41 | 42 |
    • 43 | {% endif %} 44 |
    45 | 46 |
    47 | {% endblock %} 48 | 49 | {% block extra-scripts %} 50 | {% include "infinity-pagination.html" %} 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /web/static/js/lib/class.js: -------------------------------------------------------------------------------- 1 | /* Simple JavaScript Inheritance 2 | * By John Resig http://ejohn.org/ 3 | * MIT Licensed. 4 | */ 5 | // Inspired by base2 and Prototype 6 | (function(){ 7 | var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; 8 | 9 | // The base Class implementation (does nothing) 10 | this.Class = function(){}; 11 | 12 | // Create a new Class that inherits from this class 13 | Class.extend = function(prop) { 14 | var _super = this.prototype; 15 | 16 | // Instantiate a base class (but only create the instance, 17 | // don't run the init constructor) 18 | initializing = true; 19 | var prototype = new this(); 20 | initializing = false; 21 | 22 | // Copy the properties over onto the new prototype 23 | for (var name in prop) { 24 | // Check if we're overwriting an existing function 25 | prototype[name] = typeof prop[name] == "function" && 26 | typeof _super[name] == "function" && fnTest.test(prop[name]) ? 27 | (function(name, fn){ 28 | return function() { 29 | var tmp = this._super; 30 | 31 | // Add a new ._super() method that is the same method 32 | // but on the super-class 33 | this._super = _super[name]; 34 | 35 | // The method only need to be bound temporarily, so we 36 | // remove it when we're done executing 37 | var ret = fn.apply(this, arguments); 38 | this._super = tmp; 39 | 40 | return ret; 41 | }; 42 | })(name, prop[name]) : 43 | prop[name]; 44 | } 45 | 46 | // The dummy class constructor 47 | function Class() { 48 | // All construction is actually done in the init method 49 | if ( !initializing && this.init ) 50 | this.init.apply(this, arguments); 51 | } 52 | 53 | // Populate our constructed prototype object 54 | Class.prototype = prototype; 55 | 56 | // Enforce the constructor to be what we expect 57 | Class.prototype.constructor = Class; 58 | 59 | // And make this class extendable 60 | Class.extend = arguments.callee; 61 | 62 | return Class; 63 | }; 64 | })(); -------------------------------------------------------------------------------- /web/premises/management/commands/reports_to_gml.py: -------------------------------------------------------------------------------- 1 | from django.core.management import BaseCommand 2 | from unidecode import unidecode 3 | 4 | from premises.models import Report, get_fallacy_types, Premise, Contention 5 | 6 | import networkx as nx 7 | 8 | graph = nx.Graph() 9 | 10 | 11 | class Command(BaseCommand): 12 | 13 | def normalize(self, value): 14 | return (value 15 | .replace('"', '') 16 | .rstrip(",")) 17 | 18 | def handle(self, *args, **kwargs): 19 | self.create_conjunction_graph() 20 | 21 | 22 | def create_conjunction_graph(self): 23 | fallacy_map = { 24 | unidecode(key): value 25 | for (key, value) in 26 | get_fallacy_types() 27 | } 28 | for contention in Contention.objects.all(): 29 | for premise in contention.premises.all(): 30 | fallacies = filter(None, premise.reports.values_list( 31 | 'fallacy_type', flat=True)) 32 | fallacies = [ 33 | fallacy_map[unidecode(_f)] 34 | for _f in fallacies 35 | ] 36 | fallacies_set = set(fallacies) 37 | for fallacy in fallacies_set: 38 | graph.add_edges_from( 39 | [ 40 | (unidecode(self.normalize(fallacy)), 41 | unidecode(self.normalize(_f))) 42 | for _f in fallacies_set 43 | if _f != fallacy 44 | ] 45 | ) 46 | 47 | 48 | nx.write_gml(graph, 'conjunction.gml') 49 | 50 | 51 | def create_report_graph(self): 52 | for (fallacy_type, localized) in get_fallacy_types(): 53 | node = unidecode(self.normalize(localized)) 54 | 55 | graph.add_node(node, type="fallacy", Weight=10) 56 | 57 | for premise in Premise.objects.filter( 58 | reports__fallacy_type=fallacy_type): 59 | 60 | #graph.add_node(premise.argument.pk, type="argument") 61 | 62 | #graph.add_edge(premise.argument.pk, node, type="reported") 63 | 64 | if premise.argument.channel: 65 | channel_node = unidecode(premise.argument.channel.title) 66 | 67 | graph.add_node(channel_node, type="channel", 68 | Weight=premise.argument.channel.contentions.count() * 30) 69 | graph.add_edge(channel_node, node, type="reported") 70 | 71 | 72 | nx.write_gml(graph, 'reports.gml') -------------------------------------------------------------------------------- /web/premises/migrations/0010_auto_20141027_0204.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | import django.core.validators 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('premises', '0009_merge'), 13 | ] 14 | 15 | operations = [ 16 | migrations.RemoveField( 17 | model_name='report', 18 | name='user', 19 | ), 20 | migrations.AlterField( 21 | model_name='contention', 22 | name='is_published', 23 | field=models.BooleanField(default=True), 24 | ), 25 | migrations.AlterField( 26 | model_name='contention', 27 | name='title', 28 | field=models.CharField(help_text="\xd6nermeleriyle birlikte tart\u0131\u015f\u0131labilecek, desteklenebilecek, ispatlanabilecek ya da \xe7\xfcr\xfct\xfclebilecek bir arg\xfcman.\n\xd6rnekler:\n
      \n
    • Sanat toplum i\xe7indir.
    • \n
    • Bisiklet bir ula\u015f\u0131m arac\u0131d\u0131r.
    • \n
    • Bisiklet\xe7iler trafikte bulundu\u011fu t\xfcm \u015feridi kaplamal\u0131d\u0131r.
    • \n
    • Python'\u0131n ilerlemesinde GIL b\xfcy\xfck bir engeldir.
    • \n
    ", max_length=255, verbose_name=b'Arg\xc3\xbcman'), 29 | ), 30 | migrations.AlterField( 31 | model_name='premise', 32 | name='is_approved', 33 | field=models.BooleanField(default=True, verbose_name=b'Yay\xc4\xb1nla'), 34 | ), 35 | migrations.AlterField( 36 | model_name='premise', 37 | name='text', 38 | field=models.TextField(blank=True, help_text='\xd6rnek: Bisiklet s\xfcr\xfcc\xfcs\xfc karayolunda en sa\u011f \u015feridi kullan\u0131r ve di\u011fer ta\u015f\u0131tlar ile ayn\u0131 sorumlulukla hareket eder.', null=True, verbose_name=b'\xc3\x96nermenin \xc4\xb0\xc3\xa7eri\xc4\x9fi', validators=[django.core.validators.MaxLengthValidator(300)]), 39 | ), 40 | migrations.AlterField( 41 | model_name='report', 42 | name='contention', 43 | field=models.ForeignKey(related_name=b'reports', blank=True, to='premises.Contention', null=True), 44 | ), 45 | migrations.AlterField( 46 | model_name='report', 47 | name='premise', 48 | field=models.ForeignKey(related_name=b'reports', blank=True, to='premises.Premise', null=True), 49 | ), 50 | migrations.AlterField( 51 | model_name='report', 52 | name='reporter', 53 | field=models.ForeignKey(related_name=b'reports', to=settings.AUTH_USER_MODEL), 54 | ), 55 | ] 56 | -------------------------------------------------------------------------------- /web/profiles/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | 5 | from django import forms 6 | from django.contrib.auth.forms import UserCreationForm, UserChangeForm, SetPasswordForm, PasswordChangeForm 7 | from django.utils.translation import ugettext_lazy as _ 8 | from profiles.models import Profile 9 | 10 | 11 | class RegistrationForm(UserCreationForm): 12 | username = forms.RegexField( 13 | label="Kullanıcı adınız", 14 | max_length=30, regex=re.compile(r'^[\w\s-]+$', re.LOCALE), 15 | help_text = "30 karakterden az, ascii, - ve boşluk kullanabileceginiz kullanıcı adı", 16 | error_messages = {'invalid': "ascii, - boşluk karakterleri dışında karakter girmeyiniz."} 17 | ) 18 | 19 | class Meta(UserCreationForm.Meta): 20 | fields = ("username", "email") 21 | model = Profile 22 | 23 | 24 | def clean_username(self): 25 | # Since User.username is unique, this check is redundant, 26 | # but it sets a nicer error message than the ORM. See #13147. 27 | username = self.cleaned_data["username"] 28 | try: 29 | Profile._default_manager.get(username=username) 30 | except Profile.DoesNotExist: 31 | return username 32 | raise forms.ValidationError( 33 | self.error_messages['duplicate_username'], 34 | code='duplicate_username', 35 | ) 36 | 37 | 38 | class ProfileUpdateForm(forms.ModelForm): 39 | error_messages = { 40 | 'password_mismatch': _("The two password fields didn't match."), 41 | } 42 | 43 | class Meta(UserCreationForm.Meta): 44 | fields = ("username", "email") 45 | model = Profile 46 | 47 | new_password1 = forms.CharField(label=_("New password"), 48 | required=False, 49 | widget=forms.PasswordInput) 50 | new_password2 = forms.CharField(required=False, 51 | label=_("New password confirmation"), 52 | widget=forms.PasswordInput) 53 | 54 | def clean(self): 55 | password1 = self.cleaned_data.get('new_password1') 56 | password2 = self.cleaned_data.get('new_password2') 57 | if password1 or password2: 58 | if password1 != password2: 59 | raise forms.ValidationError( 60 | self.error_messages['password_mismatch'], 61 | code='password_mismatch', 62 | ) 63 | return self.cleaned_data 64 | 65 | def save(self, commit=True): 66 | if self.cleaned_data.get("new_password1"): 67 | self.instance.set_password(self.cleaned_data['new_password1']) 68 | return super(ProfileUpdateForm, self).save(commit) 69 | -------------------------------------------------------------------------------- /web/profiles/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import django.utils.timezone 6 | from django.conf import settings 7 | import django.core.validators 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('auth', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Profile', 19 | fields=[ 20 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 21 | ('password', models.CharField(max_length=128, verbose_name='password')), 22 | ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')), 23 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 24 | ('username', models.CharField(help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, max_length=30, verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')])), 25 | ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), 26 | ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), 27 | ('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)), 28 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 29 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 30 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 31 | ('following', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), 32 | ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.', verbose_name='groups')), 33 | ('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')), 34 | ], 35 | options={ 36 | 'abstract': False, 37 | 'verbose_name': 'user', 38 | 'verbose_name_plural': 'users', 39 | }, 40 | bases=(models.Model,), 41 | ), 42 | ] 43 | -------------------------------------------------------------------------------- /web/newsfeed/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import View, TemplateView 2 | from newsfeed.constants import NEWS_TYPE_CONTENTION, NEWS_TYPE_PREMISE, NEWS_TYPE_FALLACY, NEWS_TYPE_FOLLOWING 3 | from newsfeed.models import Entry 4 | from premises.utils import int_or_zero 5 | from premises.views import HomeView 6 | 7 | 8 | class NewsfeedView(HomeView): 9 | tab_class = "newsfeed" 10 | template_name = "newsfeed.html" 11 | paginate_by = 20 12 | 13 | def get_context_data(self, **kwargs): 14 | return super(NewsfeedView, self).get_context_data( 15 | news_entries=self.get_news_entries(), 16 | **kwargs) 17 | 18 | def get_news_entries(self): 19 | if self.request.user.is_authenticated(): 20 | newsfeed = self.get_private_newsfeed() 21 | else: 22 | newsfeed = self.get_public_newsfeed() 23 | return newsfeed 24 | 25 | def has_next_page(self): 26 | # tricky a little bit. 27 | # if the page loaded full, probably there are more news 28 | # entries. if not, returns a single empty page, it's not a problem. 29 | # it's more effortless instead of get all collection for now. 30 | return (len(self.get_news_entries()) == self.paginate_by) 31 | 32 | def get_private_newsfeed(self): 33 | """ 34 | Fetches news items from the newsfeed database 35 | """ 36 | parameters = { 37 | "recipients": { 38 | "$in": [self.request.user.id] 39 | }, 40 | "news_type": { 41 | "$in": [NEWS_TYPE_CONTENTION, 42 | NEWS_TYPE_PREMISE, 43 | NEWS_TYPE_FALLACY, 44 | NEWS_TYPE_FOLLOWING] 45 | }} 46 | 47 | newsfeed = (Entry 48 | .objects 49 | .collection 50 | .find(parameters) 51 | .sort([("date_created", -1)]) 52 | .skip(self.get_offset()) 53 | .limit(self.paginate_by)) 54 | 55 | return map(Entry, newsfeed) 56 | 57 | def get_public_newsfeed(self): 58 | """ 59 | Fetches news items from the newsfeed database 60 | """ 61 | parameters = { 62 | "news_type": { 63 | "$in": [NEWS_TYPE_CONTENTION, 64 | NEWS_TYPE_PREMISE] 65 | }} 66 | 67 | newsfeed = (Entry 68 | .objects 69 | .collection 70 | .find(parameters) 71 | .sort([("date_created", -1)]) 72 | .skip(self.get_offset()) 73 | .limit(self.paginate_by)) 74 | 75 | return map(Entry, newsfeed) 76 | 77 | 78 | class PublicNewsfeedView(NewsfeedView): 79 | get_news_entries = NewsfeedView.get_public_newsfeed 80 | 81 | def get_context_data(self, **kwargs): 82 | return super(PublicNewsfeedView, self).get_context_data( 83 | newsfeed_is_public=True, 84 | **kwargs) 85 | -------------------------------------------------------------------------------- /web/premises/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Comment', 17 | fields=[ 18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 19 | ('text', models.TextField()), 20 | ('date_creation', models.DateTimeField(auto_now_add=True)), 21 | ('is_active', models.BooleanField(default=True)), 22 | ], 23 | options={ 24 | }, 25 | bases=(models.Model,), 26 | ), 27 | migrations.CreateModel( 28 | name='Contention', 29 | fields=[ 30 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 31 | ('title', models.CharField(max_length=255)), 32 | ('slug', models.SlugField(max_length=255)), 33 | ('description', models.TextField(null=True, blank=True)), 34 | ('owner', models.CharField(max_length=255, null=True, blank=True)), 35 | ('sources', models.TextField(null=True, blank=True)), 36 | ('is_featured', models.BooleanField(default=False)), 37 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 38 | ], 39 | options={ 40 | }, 41 | bases=(models.Model,), 42 | ), 43 | migrations.CreateModel( 44 | name='Premise', 45 | fields=[ 46 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 47 | ('premise_type', models.IntegerField(choices=[(0, '\u0130tiraz'), (1, 'Destek'), (2, 'Bilgilendirme')])), 48 | ('sources', models.TextField(null=True, blank=True)), 49 | ('is_approved', models.BooleanField(default=False)), 50 | ('argument', models.ForeignKey(to='premises.Contention')), 51 | ('parent', models.ForeignKey(related_name=b'children', to='premises.Premise')), 52 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 53 | ], 54 | options={ 55 | }, 56 | bases=(models.Model,), 57 | ), 58 | migrations.AddField( 59 | model_name='comment', 60 | name='premise', 61 | field=models.ForeignKey(to='premises.Premise'), 62 | preserve_default=True, 63 | ), 64 | migrations.AddField( 65 | model_name='comment', 66 | name='user', 67 | field=models.ForeignKey(to=settings.AUTH_USER_MODEL), 68 | preserve_default=True, 69 | ), 70 | ] 71 | -------------------------------------------------------------------------------- /web/premises/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from premises.views import (ContentionDetailView, HomeView, 3 | ArgumentCreationView, PremiseCreationView, 4 | PremiseDeleteView, ContentionJsonView, 5 | PremiseEditView, ArgumentUpdateView, 6 | ArgumentPublishView, ArgumentUnpublishView, 7 | ArgumentDeleteView, AboutView, NewsView, 8 | UpdatedArgumentsView, ReportView, 9 | ControversialArgumentsView, TosView, SearchView, 10 | NotificationsView, PremiseSupportView, PremiseUnsupportView) 11 | 12 | 13 | urlpatterns = patterns('', 14 | url(r'^$', HomeView.as_view(), name='home'), 15 | url(r'^notifications$', NotificationsView.as_view(), name='notifications'), 16 | url(r'^news$', NewsView.as_view(), 17 | name='contentions_latest'), 18 | url(r'^search', SearchView.as_view(), 19 | name='contentions_search'), 20 | url(r'^updated$', UpdatedArgumentsView.as_view(), 21 | name='contentions_updated'), 22 | url(r'^controversial', ControversialArgumentsView.as_view(), 23 | name='contentions_controversial'), 24 | url(r'^about$', 25 | AboutView.as_view(), 26 | name='about'), 27 | url(r'^tos$', 28 | TosView.as_view(), 29 | name='tos'), 30 | url(r'^new-argument$', 31 | ArgumentCreationView.as_view(), 32 | name='new_argument'), 33 | url(r'^(?P[\w-]+)/edit$', 34 | ArgumentUpdateView.as_view(), 35 | name='contention_edit'), 36 | url(r'^(?P[\w-]+)\.json$', 37 | ContentionJsonView.as_view(), 38 | name='contention_detail_json'), 39 | url(r'^(?P[\w-]+)$', 40 | ContentionDetailView.as_view(), 41 | name='contention_detail'), 42 | url(r'^(?P[\w-]+)/premises/(?P[0-9]+)/unsupport', 43 | PremiseUnsupportView.as_view(), 44 | name='unsupport_premise'), 45 | url(r'^(?P[\w-]+)/premises/(?P[0-9]+)/support', 46 | PremiseSupportView.as_view(), 47 | name='support_premise'), 48 | url(r'^(?P[\w-]+)/premises/(?P[0-9]+)/delete', 49 | PremiseDeleteView.as_view(), 50 | name='delete_premise'), 51 | url(r'^(?P[\w-]+)/premises/(?P[0-9]+)/report', 52 | ReportView.as_view(), 53 | name='report_premise'), 54 | url(r'^(?P[\w-]+)/premises/(?P[0-9]+)/new', 55 | PremiseCreationView.as_view(), 56 | name='insert_premise'), 57 | url(r'^(?P[\w-]+)/premises/(?P[0-9]+)', 58 | PremiseEditView.as_view(), 59 | name='edit_premise'), 60 | url(r'^(?P[\w-]+)/premises/new', 61 | PremiseCreationView.as_view(), 62 | name='new_premise'), 63 | url(r'^(?P[\w-]+)/publish', 64 | ArgumentPublishView.as_view(), 65 | name='contention_publish'), 66 | url(r'^(?P[\w-]+)/unpublish', 67 | ArgumentUnpublishView.as_view(), 68 | name='contention_unpublish'), 69 | url(r'^(?P[\w-]+)/delete', 70 | ArgumentDeleteView.as_view(), 71 | name='contention_delete'), 72 | ) 73 | -------------------------------------------------------------------------------- /web/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block title %}Argüman Analizi Platformu{% endblock %} 5 | 6 | {% load static %} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% block header %} 15 |
    16 | {% if announcements.exists %} 17 | 22 | {% endif %} 23 | 27 | 42 |
    43 | {% endblock %} 44 | {% block content %} 45 | {% endblock %} 46 | {% block footer %} 47 | 56 | {% endblock %} 57 | 58 | 59 | 60 | 61 | 62 | {% block extra-scripts %}{% endblock %} 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /web/static/js/lib/underscore-template.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Underscore.js templates as a standalone implementation. 3 | JavaScript micro-templating, similar to John Resig's implementation. 4 | Underscore templates documentation: http://documentcloud.github.com/underscore/#template 5 | Modified by marlun78 6 | 7 | note: 8 | I changed the `text` variable as `selector` for get template 9 | source from an element. -fatih 10 | 11 | */ 12 | (function ($) { 13 | 14 | 'use strict'; 15 | 16 | // By default, Underscore uses ERB-style template delimiters, change the 17 | // following template settings to use alternative delimiters. 18 | var settings = { 19 | evaluate: /<%([\s\S]+?)%>/g, 20 | interpolate: /<%=([\s\S]+?)%>/g, 21 | escape: /<%-([\s\S]+?)%>/g 22 | }; 23 | 24 | // When customizing `templateSettings`, if you don't want to define an 25 | // interpolation, evaluation or escaping regex, we need one that is 26 | // guaranteed not to match. 27 | var noMatch = /.^/; 28 | 29 | // Certain characters need to be escaped so that they can be put into a 30 | // string literal. 31 | var escapes = { 32 | '\\': '\\', 33 | "'": "'", 34 | 'r': '\r', 35 | 'n': '\n', 36 | 't': '\t', 37 | 'u2028': '\u2028', 38 | 'u2029': '\u2029' 39 | }; 40 | 41 | for (var p in escapes) { 42 | escapes[escapes[p]] = p; 43 | } 44 | 45 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 46 | var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g; 47 | 48 | var tmpl = function (selector, data, objectName) { 49 | settings.variable = objectName; 50 | 51 | var text = $(selector).html() || ""; 52 | 53 | // Compile the template source, taking care to escape characters that 54 | // cannot be included in a string literal and then unescape them in code 55 | // blocks. 56 | var source = "__p+='" + text 57 | .replace(escaper, function (match) { 58 | return '\\' + escapes[match]; 59 | }) 60 | .replace(settings.escape || noMatch, function (match, code) { 61 | return "'+\n_.escape(" + unescape(code) + ")+\n'"; 62 | }) 63 | .replace(settings.interpolate || noMatch, function (match, code) { 64 | return "'+\n(" + unescape(code) + ")+\n'"; 65 | }) 66 | .replace(settings.evaluate || noMatch, function (match, code) { 67 | return "';\n" + unescape(code) + "\n;__p+='"; 68 | }) + "';\n"; 69 | 70 | // If a variable is not specified, place data values in local scope. 71 | if (!settings.variable) { 72 | source = 'with(obj||{}){\n' + source + '}\n'; 73 | } 74 | 75 | source = "var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + source + "return __p;\n"; 76 | 77 | var render = new Function(settings.variable || 'obj', source); 78 | 79 | if (data) { 80 | return render(data); 81 | } 82 | 83 | var template = function (data) { 84 | return render.call(this, data); 85 | }; 86 | 87 | // Provide the compiled function source as a convenience for build time 88 | // precompilation. 89 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 90 | 91 | return template; 92 | }; 93 | 94 | window.template = tmpl; 95 | 96 | }(window.jQuery)); -------------------------------------------------------------------------------- /web/templates/auth/profile.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load gravatar %} 3 | 4 | {% block content %} 5 | 67 | {% endblock %} 68 | 69 | {% block extra-scripts %} 70 | {{ block.super }} 71 | 89 | 90 | {% endblock %} -------------------------------------------------------------------------------- /web/premises/migrations/0016_report_fallacy_type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0015_premise_collapsed'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='report', 16 | name='fallacy_type', 17 | field=models.CharField(max_length=255, null=True, verbose_name=[['Serbest Safsatalar', '\u0130nformel fallacies'], ['Cinasl\u0131 Safsata', 'Fallacy of Equivocation'], ['\xc7ok Anlaml\u0131l\u0131k Safsatas\u0131', 'Fallacy of Amphiboly'], ['Vurgulama Safsatas\u0131', 'Fallacy of Accent'], ['\xd6zelle\u015ftirme Safsatas\u0131', 'Fallacy Of Accident'], ['Genelle\u015ftirme Safsatas\u0131', 'Fallacy of Converse Accident'], ['B\xfct\xfcnleme Safsatas\u0131', 'Fallacy of Composition'], ['\u0130ndirgeme Safsatas\u0131', 'Fallacy Of Division'], ['K\u0131s\u0131r D\xf6ng\xfc Safsatas\u0131', 'Begging The Question,'], ['Alakas\u0131z Sonu\xe7 Safsatas\u0131', 'Irrelevant Conclusion'], ['\u0130ddiay\u0131 Zay\u0131flatma Safsatas\u0131', 'Fallacy of the Straw-Man'], ['Alakas\u0131z Ama\xe7 Safsatas\u0131', 'Fallacy of \u0130rrelevant Purpose'], ['Konuyu Sapt\u0131rma Safsatas\u0131', 'Fallacy of Red Herring'], ['Adam Karalama Safsatas\u0131', 'Argument Against the Man'], ['Niteliksel Adam Karalama', 'Circumstantial Ad Hominem'], ['\u201cSen de\u201d Safsatas\u0131', 'Fallacy Of \u201cYou Also\u201d'], ['Dolduru\u015fa Getirme Safsatas\u0131', 'Poisoning The Well'], ['Devede Kulak Safsatas\u0131', 'Fallacy Of The Beard'], ['Ya Siyah Ya Beyaz Safsatas\u0131', 'Black And White Fallacy'], ['\u0130spatlama Mecburiyeti Safsatas\u0131', 'Argument From Ignorance'], ['Felaket Tellall\u0131\u011f\u0131 Safsatas\u0131', 'Fallacy of Slippery Slope'], ['\u0130mal\u0131 Soru Safsatas\u0131', 'Complex Question'], ['\xc7ok Sorulu Safsata', 'Fallacy Of Many Questions'], ['S\u0131n\u0131rl\u0131 Se\xe7enek Safsatas\u0131', 'Fallacy of Limited Alternatives'], ['Yanl\u0131\u015f Sebep Safsatas\u0131', 'Fallacy of False Cause'], ['\xd6ncesinde Safsatas\u0131', 'Fallacy of \u201cPrevious This\u201d'], ['M\xfc\u015fterek Etki', 'Joint Effect'], ['\u0130hmal Edilebilir Neden Safsatas\u0131', 'Genuine but Insignificant Cause'], ['Yanl\u0131\u015f Y\xf6n Safsatas\u0131', 'Wrong Direction,'], ['Karma\u015f\u0131k Nedenler Safsatas\u0131', 'Complex Cause'], ['Yetersiz \xd6rnek Safsatas\u0131', '(Fallacy of Insufficient Sample)'], ['Temsil Etmeyen \xd6rnek Safsatas\u0131', 'Unrepresentative Sample'], ['Yanl\u0131\u015f Benzetme Safsatas\u0131', 'False Analogy'], ['Yok Sayma Safsatas\u0131', 'Slothful Induction'], ['S\xfcmen Alt\u0131 Safsatas\u0131', 'Fallacy of Slanting'], ['Kumarbaz Safsatas\u0131', 'Gambler\u2019s Fallacy'], ['Bir Bilen Safsatas\u0131', 'Argument To Authority'], ['\u0130rrasyonel Otorite Safsatas\u0131', 'Fallacy Of Unqualified Source'], ['\u0130nanca Ba\u015fvurma Safsatas\u0131', 'Appeal to Belief'], ['Ortak Tutuma Ba\u015fvurma Safsatas\u0131', 'Appeal To Common Practice'], ['Grup Bask\u0131s\u0131 Safsatas\u0131', 'Bandwagon, Peer Pressure'], ['Faydac\u0131 Safsata', 'Pragmatic Fallacy'], ['Be\u011fendirme Safsatas\u0131', 'Appeal To Personal \u0130nterests'], ['Dayatma Safsatas\u0131', 'Fallacy Of \u201c\u0130s\u201d To \u201cOught\u201d'], ['Mazruf De\u011fil Zarf Safsatas\u0131', 'Style Over Substance'], ['Genetik Safsatas\u0131', 'Genetic Fallacy'], ['Tehdit Safsatas\u0131', 'Argument From Force'], ['Duygu S\xf6m\xfcr\xfcs\xfc', 'Argument To Pity'], ['\xd6nyarg\u0131l\u0131 Dil Safsatas\u0131', 'Prejudicial Language'], ['Mazeret Safsatas\u0131', 'Fallacy Of Special Pleading']]), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /web/premises/migrations/0007_auto_20141020_2215.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('premises', '0006_contention_is_published'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='premise', 16 | name='sibling_count', 17 | field=models.IntegerField(default=1), 18 | preserve_default=True, 19 | ), 20 | migrations.AlterField( 21 | model_name='contention', 22 | name='description', 23 | field=models.TextField(null=True, verbose_name=b'Ek bilgiler', blank=True), 24 | ), 25 | migrations.AlterField( 26 | model_name='contention', 27 | name='owner', 28 | field=models.CharField(help_text='Arg\xfcman\u0131n sahibi. Bir kurum, kitap ya da ki\u015fi olabilir.\n\xd6rnekler:\n
      \n
    • T.C. Anayasas\u0131
    • \n
    • T\xfcrk Dil Kurumu
    • \n
    • Friedrich Nietzsche
    • \n
    • Piet Mondiran
    • \n
    • Aziz Nesin
    • \n
    \nE\u011fer bir de\u011fer girilmemi\u015fse arg\xfcman\u0131n sahibi arg\xfcman\u0131 ekleyen olarak g\xf6r\xfcl\xfcr.', max_length=255, null=True, verbose_name=b'Orijinal s\xc3\xb6ylem', blank=True), 29 | ), 30 | migrations.AlterField( 31 | model_name='contention', 32 | name='sources', 33 | field=models.TextField(help_text='Arg\xfcman\u0131n kayna\u011f\u0131. Bir URL, kitap ad\u0131 ya da dergi ad\u0131 olabilir.\nBu alan \xf6nemlidir, kaynaks\u0131z ve tart\u0131\u015fmal\u0131 bir arg\xfcman/\xf6nerme yay\u0131ndan kald\u0131r\u0131l\u0131r.', null=True, verbose_name=b'Kaynaklar', blank=True), 34 | ), 35 | migrations.AlterField( 36 | model_name='contention', 37 | name='title', 38 | field=models.CharField(help_text="\xd6nermeleriyle birlikte tart\u0131\u015f\u0131labilecek, desteklenebilecek/ispatlanabilecek ya da \xe7\xfcr\xfct\xfclebilecek bir arg\xfcman.\n\xd6rnekler:\n
      \n
    • Sanat toplum i\xe7indir.
    • \n
    • Bisiklet bir ula\u015f\u0131m arac\u0131d\u0131r.
    • \n
    • Bisiklet\xe7iler trafikte bulundu\u011fu t\xfcm \u015feridi kaplamal\u0131d\u0131r.
    • \n
    • Python'\u0131n ilerlemesinde GIL b\xfcy\xfck bir engeldir.
    • \n
    ", max_length=255, verbose_name=b'Arg\xc3\xbcman'), 39 | ), 40 | migrations.AlterField( 41 | model_name='premise', 42 | name='premise_type', 43 | field=models.IntegerField(default=1, help_text='\xd6nermenin tipi belirtilmesi gerekir. De\u011ferler \u015funlard\u0131r:\n
      \n
    • \n ama: itiraz ve \xe7\xfcr\xfctme i\xe7in kullan\u0131l\u0131r\n
    • \n
    • \n \xe7\xfcnk\xfc: destek/kan\u0131t i\xe7in kullan\u0131l\u0131r\n
    • \n
    • \n ancak: bir \xf6nermeye ek bilgi ya da durum belirtilmesi i\xe7in kullan\u0131l\u0131r.\n
    • \n
    ', verbose_name=b'\xc3\x96nerme Tipi', choices=[(0, 'ama'), (1, '\xe7\xfcnk\xfc'), (2, 'ancak')]), 44 | ), 45 | migrations.AlterField( 46 | model_name='premise', 47 | name='sources', 48 | field=models.TextField(help_text='\xd6rnek: T.C. Karayollar\u0131 Trafik Kanunu 2918/46. Maddesi', null=True, verbose_name=b'Kaynaklar', blank=True), 49 | ), 50 | migrations.AlterField( 51 | model_name='premise', 52 | name='text', 53 | field=models.TextField(help_text='\xd6rnek: Bisiklet s\xfcr\xfcc\xfcs\xfc karayolunda en sa\u011f \u015feridi kullan\u0131r ve di\u011fer ta\u015f\u0131tlar ile ayn\u0131 sorumlulukla hareket eder.', null=True, verbose_name=b'\xc3\x96nermenin \xc4\xb0\xc3\xa7eri\xc4\x9fi', blank=True), 54 | ), 55 | ] 56 | -------------------------------------------------------------------------------- /web/main/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for arguman project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.6/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.6/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = 'qlp_henm3k-$7u@9b(@coqgpd1-2xmtox%a8_#*r9=0wh5d0oo' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = True 24 | 25 | TEMPLATE_DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = ( 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'django.contrib.humanize', 40 | 41 | 'social_auth', 42 | 'django_gravatar', 43 | 44 | 'profiles', 45 | 'premises', 46 | 'newsfeed', 47 | 'blog', 48 | 49 | ) 50 | 51 | MIDDLEWARE_CLASSES = ( 52 | 'django.contrib.sessions.middleware.SessionMiddleware', 53 | 'django.middleware.common.CommonMiddleware', 54 | 'django.middleware.csrf.CsrfViewMiddleware', 55 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 56 | 'django.contrib.messages.middleware.MessageMiddleware', 57 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 58 | ) 59 | 60 | ROOT_URLCONF = 'main.urls' 61 | 62 | WSGI_APPLICATION = 'main.wsgi.application' 63 | 64 | 65 | # Database 66 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases 67 | 68 | DATABASES = { 69 | 'default': { 70 | 'ENGINE': 'django.db.backends.sqlite3', 71 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 72 | } 73 | } 74 | 75 | # Internationalization 76 | # https://docs.djangoproject.com/en/1.6/topics/i18n/ 77 | 78 | LANGUAGE_CODE = 'TR-tr' 79 | 80 | TIME_ZONE = 'UTC' 81 | 82 | USE_I18N = True 83 | 84 | USE_L10N = True 85 | 86 | USE_TZ = True 87 | 88 | 89 | # Static files (CSS, JavaScript, Images) 90 | # https://docs.djangoproject.com/en/1.6/howto/static-files/ 91 | 92 | STATIC_URL = '/static/' 93 | 94 | STATICFILES_DIRS = ( 95 | os.path.join(os.path.dirname(__file__), "../static"), 96 | ) 97 | 98 | 99 | TEMPLATE_DIRS = ( 100 | os.path.join(os.path.dirname(__file__), "../templates"), 101 | ) 102 | 103 | 104 | # Social Auth Settings 105 | AUTHENTICATION_BACKENDS = ( 106 | 'social_auth.backends.twitter.TwitterBackend', 107 | 'django.contrib.auth.backends.ModelBackend', 108 | ) 109 | 110 | 111 | AUTH_USER_MODEL = 'profiles.Profile' 112 | 113 | 114 | TWITTER_CONSUMER_KEY = None # defined in settings_local.py 115 | TWITTER_CONSUMER_SECRET = None # defined in settings_local.py 116 | 117 | LOGIN_REDIRECT_URL = '/' 118 | SOCIAL_AUTH_COMPLETE_URL_NAME = 'socialauth_complete' 119 | SOCIAL_AUTH_ASSOCIATE_URL_NAME = 'socialauth_associate_complete' 120 | SOCIAL_AUTH_PIPELINE = ( 121 | 'social_auth.backends.pipeline.social.social_auth_user', 122 | 'social_auth.backends.pipeline.associate.associate_by_email', 123 | 'social_auth.backends.pipeline.user.get_username', 124 | 'social_auth.backends.pipeline.user.create_user', 125 | 'social_auth.backends.pipeline.social.associate_user', 126 | 'social_auth.backends.pipeline.social.load_extra_data', 127 | 'social_auth.backends.pipeline.user.update_user_details', 128 | ) 129 | 130 | MONGODB_HOST = "localhost" 131 | MONGODB_DATABASE = "arguman" 132 | 133 | SITE_URL = "arguman.org" 134 | 135 | # Markitup Settings 136 | MARKITUP_SET = 'markitup/sets/markdown' 137 | MARKITUP_FILTER = ('markdown.markdown', {'safe_mode': False}) 138 | 139 | BLOG_FEED_TITLE = "Arguman.org Blog'u" 140 | BLOG_FEED_DESCRIPTION = "Arguman analizi platformu" 141 | BLOG_URL = "http://arguman.org/blog" 142 | 143 | try: 144 | from settings_local import * 145 | except ImportError: 146 | print "settings_local.py not found!" 147 | -------------------------------------------------------------------------------- /web/static/js/lib/hipo.infinity-scroll.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Infinite Scroll Plugin 3 | * 4 | * Fatih Erikli (fatih at hipo dot biz) - July 31, 2012 5 | */ 6 | 7 | window.hipo = window.hipo || {}; 8 | 9 | hipo.InfinityScroll = Class.extend({ 10 | 11 | // settings 12 | loader_image : "", 13 | content_selector : "", 14 | pagination_selector : ".pagination", 15 | next_link_selector : ".pagination a.next", 16 | on_page_load : function () {}, 17 | max_page: null, 18 | 19 | // states 20 | loader : null, 21 | active_page : 1, 22 | 23 | // constants 24 | FOOTER_POSITION_THRESHOLD : 0, 25 | MOBILE_FOOTER_POSITION_THRESHOLD : 1000, 26 | 27 | init : function (options) { 28 | 29 | $.extend(this, options); 30 | 31 | this.hide_pagination(); 32 | this.check_scroll(this.load_page.bind(this)); 33 | this.prepare_loader(); 34 | 35 | }, 36 | 37 | load_content : function (response) { 38 | 39 | var content = $(this.content_selector, response).html(); 40 | $(this.content_selector).append(content); 41 | 42 | }, 43 | 44 | load_page : function () { 45 | 46 | var next_page = this.get_next_page(); 47 | 48 | if (this.max_page) { 49 | if (this.active_page >= this.max_page) { 50 | this.show_pagination(); 51 | return; 52 | } 53 | } 54 | 55 | if (next_page) { 56 | 57 | this.remove_pagination(); 58 | this.show_loader(); 59 | 60 | var paginate = function (response) { 61 | this.load_content(response); 62 | this.hide_pagination(); 63 | this.hide_loader(); 64 | 65 | // events 66 | this.on_page_load(); 67 | }.bind(this); 68 | 69 | $.get(next_page, paginate.bind(this), 'html'); 70 | this.active_page++; 71 | 72 | } 73 | 74 | this.on_page_load && this.on_page_load(); 75 | 76 | }, 77 | 78 | get_next_page : function () { 79 | 80 | if ($(this.next_link_selector).length) { 81 | return $(this.next_link_selector).attr("href"); 82 | } else { 83 | return false; 84 | } 85 | 86 | }, 87 | 88 | check_scroll : function (callback) { 89 | 90 | $(window).scroll(function() { 91 | 92 | if ($(window).scrollTop() + $(window).height() > 93 | this.get_doc_height() - this.get_footer_threshold() ) { 94 | callback(); 95 | } 96 | 97 | }.bind(this)); 98 | 99 | }, 100 | 101 | hide_pagination : function () { 102 | 103 | $(this.pagination_selector).hide(); 104 | 105 | }, 106 | 107 | show_pagination : function () { 108 | 109 | $(this.pagination_selector).show(); 110 | 111 | }, 112 | 113 | remove_pagination : function () { 114 | 115 | $(this.pagination_selector).remove() 116 | 117 | }, 118 | 119 | prepare_loader : function () { 120 | 121 | this.loader = $("
    ").css({ 122 | "display": "none", 123 | "text-align": "center", 124 | "padding": "10px", 125 | "clear": "both" 126 | }).append($("", { 127 | "src": this.loader_image 128 | })); 129 | 130 | $(this.content_selector).after(this.loader); 131 | 132 | }, 133 | 134 | show_loader : function () { 135 | 136 | this.loader.show(); 137 | }, 138 | 139 | hide_loader : function () { 140 | 141 | this.loader.hide(); 142 | 143 | }, 144 | 145 | get_footer_threshold : function () { 146 | 147 | return this.is_mobile_device() ? 148 | this.MOBILE_FOOTER_POSITION_THRESHOLD : this.FOOTER_POSITION_THRESHOLD; 149 | 150 | }, 151 | 152 | get_doc_height : function () { 153 | 154 | var D = document; 155 | 156 | return Math.max( 157 | Math.max(D.body.scrollHeight, D.documentElement.scrollHeight), 158 | Math.max(D.body.offsetHeight, D.documentElement.offsetHeight), 159 | Math.max(D.body.clientHeight, D.documentElement.clientHeight) 160 | ); 161 | 162 | }, 163 | 164 | is_mobile_device : function () { 165 | 166 | return navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad|android)/); 167 | 168 | } 169 | 170 | }); -------------------------------------------------------------------------------- /web/profiles/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.contrib.auth import logout, login, authenticate 4 | from django.contrib.auth.forms import AuthenticationForm 5 | from django.contrib.auth.models import User 6 | from django.core.urlresolvers import reverse 7 | from django.http import HttpResponse 8 | from django.views.generic import FormView, CreateView, RedirectView, DetailView, UpdateView 9 | 10 | from profiles.forms import RegistrationForm, ProfileUpdateForm 11 | from profiles.models import Profile 12 | from profiles.signals import follow_done, unfollow_done 13 | from premises.models import Contention, Report 14 | 15 | 16 | class RegistrationView(CreateView): 17 | form_class = RegistrationForm 18 | template_name = "auth/register.html" 19 | 20 | def form_valid(self, form): 21 | response = super(RegistrationView, self).form_valid(form) 22 | user = authenticate(username=form.cleaned_data["username"], 23 | password=form.cleaned_data["password1"]) 24 | login(self.request, user) 25 | return response 26 | 27 | def get_success_url(self): 28 | return reverse("home") 29 | 30 | 31 | class LoginView(FormView): 32 | form_class = AuthenticationForm 33 | template_name = "auth/login.html" 34 | 35 | def form_valid(self, form): 36 | login(self.request, form.get_user()) 37 | return super(LoginView, self).form_valid(form) 38 | 39 | def get_success_url(self): 40 | return self.request.GET.get("next") or reverse("home") 41 | 42 | def get_context_data(self, **kwargs): 43 | context = super(LoginView, self).get_context_data(**kwargs) 44 | context["next"] = self.request.GET.get("next", "") 45 | return context 46 | 47 | 48 | class LogoutView(RedirectView): 49 | def get(self, request, *args, **kwargs): 50 | logout(request) 51 | return super(LogoutView, self).get(request, *args, **kwargs) 52 | 53 | def get_redirect_url(self, **kwargs): 54 | return reverse("home") 55 | 56 | 57 | class ProfileDetailView(DetailView): 58 | slug_field = 'username' 59 | slug_url_kwarg = 'username' 60 | context_object_name = "profile" 61 | model = Profile 62 | 63 | def get_context_data(self, **kwargs): 64 | """ 65 | Adds extra context to template 66 | """ 67 | user = self.get_object() 68 | contentions = Contention.objects.filter( 69 | premises__user=user 70 | ).distinct() 71 | 72 | if self.request.user != user: 73 | contentions = contentions.filter(is_published=True) 74 | 75 | can_follow = self.request.user != user 76 | 77 | if self.request.user.is_authenticated(): 78 | is_followed = self.request.user.following.filter(pk=user.id).exists() 79 | else: 80 | is_followed = False 81 | return super(ProfileDetailView, self).get_context_data( 82 | can_follow=can_follow, 83 | is_followed=is_followed, 84 | contentions=contentions) 85 | 86 | def delete(self, request, **kwargs): 87 | """ 88 | - Removes `FollowedProfile` object for authenticated user. 89 | - Fires unfollow_done signal 90 | """ 91 | user = self.get_object() 92 | 93 | request.user.following.remove(user) 94 | 95 | unfollow_done.send(sender=self, follower=request.user, following=user) 96 | 97 | return HttpResponse(json.dumps({ 98 | "success": True 99 | })) 100 | 101 | def post(self, request, **kwargs): 102 | """ 103 | - Creates `FollowedProfile` object for authenticated user. 104 | - Fires follow_done signal 105 | """ 106 | user = self.get_object() 107 | 108 | if user.followers.filter(pk=request.user.pk).exists(): 109 | return HttpResponse(json.dumps({ 110 | "error": "You already following this people." 111 | })) 112 | 113 | request.user.following.add(user) 114 | 115 | follow_done.send(sender=self, follower=request.user, following=user) 116 | 117 | return HttpResponse(json.dumps({ 118 | "success": True 119 | })) 120 | 121 | 122 | class ProfileUpdateView(UpdateView): 123 | template_name = "auth/update.html" 124 | form_class = ProfileUpdateForm 125 | 126 | def get_object(self, queryset=None): 127 | return self.request.user 128 | 129 | def get_success_url(self): 130 | return reverse("auth_profile", args=[self.request.user.username]) 131 | -------------------------------------------------------------------------------- /web/templates/premises/tree_view.html: -------------------------------------------------------------------------------- 1 |
      2 | {% for premise in premises %} 3 |
    • 4 |
      5 |
      6 | {{ premise.get_premise_type_display }} 7 |
      8 | 9 |
      10 | {{ premise.formatted_text|safe }} 11 |
      12 | 13 |
      14 | {% if premise.sources %} 15 | kaynaklar: 16 |

      {{ premise.sources|urlize }}

      17 | {% endif %} 18 | gönderen: 19 |

      20 | {{ premise.user }}

      21 | 22 | {% if premise.reports.exists %} 23 |
      24 | {{ premise.reports.count }} safsata bildirimi yapıldı. 25 | 26 | {% if premise.fallacies %} 27 | {% for fallacy in premise.fallacies %} 28 |
      {{ fallacy }}
      29 | {% endfor %} 30 | {% endif %} 31 | 32 |
      33 | {% endif %} 34 | 35 | {% if premise.supporters.exists %} 36 |
      37 | {{ premise.supporters.count }} destekleyen var: 38 |
      39 | {% for supporter in premise.recent_supporters %}{% if not forloop.first %}, {% endif %} 40 | {{ supporter.username }}{% endfor %} 41 | {% if premise.supporters.count > 5 %} 42 | ve diğerleri. 43 | {% endif %} 44 |
      45 |
      46 | {% endif %} 47 |
      48 | {% if edit_mode or user == premise.user %} 49 |
      50 | önerme ekle 51 | düzenle 52 |
      53 | {% csrf_token %} 54 | 57 |
      58 |
      59 | {% else %} 60 | {% if user.is_authenticated %} 61 |
      62 | önerme gönder 63 | {% if premise.user != user %} 64 | {% if user in premise.supporters.all %} 65 |
      66 | {% csrf_token %} 67 | 68 |
      69 | {% else %} 70 |
      71 | {% csrf_token %} 72 | 73 |
      74 | {% endif %} 75 | {% endif %} 76 |
      77 | {% endif %} 78 | {% endif %} 79 | 80 | {% if user.is_authenticated %} 81 |
      82 | safsata bildir 83 |
      84 | {% endif %} 85 | 86 |
      87 | {% if premise.children.exists %} 88 | {% with template_name="premises/tree_view.html" premises=premise.published_children %} 89 | {% include template_name %} 90 | {% endwith %} 91 | {% endif %} 92 |
    • 93 | {% endfor %} 94 |
    -------------------------------------------------------------------------------- /web/templates/premises/contention_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static %} 3 | 4 | {% block title %}{{ contention.title }}{% endblock %} 5 | 6 | {% block header %} 7 | 68 | {% endblock %} 69 | 70 | {% block content %} 71 | 72 |
    73 | 74 | 75 |
    76 | 77 |
    78 |

    harita üzerinde gezinmek için yön tuşlarını kullanabilirsiniz.

    79 |
    80 | 81 |
    82 | 83 |
    84 |
    85 | {{ contention }} 86 |
    87 | {% if user.is_authenticated %} 88 | önerme ekle 89 | {% endif %} 90 |
    91 |
    92 |
    93 | 94 |
    95 |
      96 |
    • 97 |
      98 | {% with template_name="premises/tree_view.html"%} 99 | {% with premises=contention.published_premises.all %} 100 | {% include template_name %} 101 | {% endwith %} 102 | {% endwith %} 103 |
    • 104 |
    105 |
    106 | 107 |
    108 | {% endblock %} 109 | 110 | {% block extra-scripts %} 111 | 132 | {% endblock %} 133 | 134 | 135 | {% block footer %}{% endblock %} 136 | -------------------------------------------------------------------------------- /web/profiles/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf import settings 4 | from django.contrib.auth.models import AbstractUser 5 | from django.core.exceptions import ObjectDoesNotExist 6 | from django.db import models 7 | from django.dispatch import receiver 8 | from django.template.loader import render_to_string 9 | from premises.models import Contention, Report, Premise 10 | from premises.signals import (reported_as_fallacy, added_premise_for_premise, 11 | added_premise_for_contention, supported_a_premise) 12 | from profiles.signals import follow_done 13 | 14 | 15 | class Profile(AbstractUser): 16 | following = models.ManyToManyField("self", symmetrical=False) 17 | 18 | @property 19 | def followers(self): 20 | # todo: find a way to make reverse relationships 21 | # with symmetrical false option 22 | return Profile.objects.filter(following=self) 23 | 24 | 25 | NOTIFICATION_ADDED_PREMISE_FOR_CONTENTION = 0 26 | NOTIFICATION_ADDED_PREMISE_FOR_PREMISE = 1 27 | NOTIFICATION_REPORTED_AS_FALLACY = 2 28 | NOTIFICATION_FOLLOWED_A_PROFILE = 3 29 | NOTIFICATION_SUPPORTED_A_PREMISE = 4 30 | 31 | NOTIFICATION_TYPES = ( 32 | (NOTIFICATION_ADDED_PREMISE_FOR_CONTENTION, 33 | "added-premise-for-contention"), 34 | (NOTIFICATION_ADDED_PREMISE_FOR_PREMISE, 35 | "added-premise-for-premise"), 36 | (NOTIFICATION_REPORTED_AS_FALLACY, 37 | "reported-as-fallacy"), 38 | (NOTIFICATION_FOLLOWED_A_PROFILE, 39 | "followed"), 40 | (NOTIFICATION_SUPPORTED_A_PREMISE, 41 | "supported-a-premise"), 42 | ) 43 | 44 | 45 | class Notification(models.Model): 46 | # sender can be `null` for system notifications 47 | sender = models.ForeignKey(settings.AUTH_USER_MODEL, 48 | null=True, blank=True, 49 | related_name="sent_notifications") 50 | recipient = models.ForeignKey(settings.AUTH_USER_MODEL, 51 | related_name="notifications") 52 | date_created = models.DateTimeField(auto_now_add=True) 53 | notification_type = models.IntegerField(choices=NOTIFICATION_TYPES) 54 | is_read = models.BooleanField(default=False) 55 | target_object_id = models.IntegerField(null=True, blank=True) 56 | 57 | class Meta: 58 | ordering = ['is_read', '-date_created'] 59 | 60 | def get_target_object(self): 61 | model = { 62 | NOTIFICATION_ADDED_PREMISE_FOR_CONTENTION: Premise, 63 | NOTIFICATION_ADDED_PREMISE_FOR_PREMISE: Premise, 64 | NOTIFICATION_REPORTED_AS_FALLACY: Report, 65 | NOTIFICATION_FOLLOWED_A_PROFILE: Profile, 66 | NOTIFICATION_SUPPORTED_A_PREMISE: Premise, 67 | }.get(self.notification_type) 68 | 69 | try: 70 | instance = model.objects.get(pk=self.target_object_id) 71 | except ObjectDoesNotExist: 72 | instance = None 73 | 74 | return instance 75 | 76 | def render(self): 77 | template_name = ("notifications/%s.html" % 78 | self.get_notification_type_display()) 79 | return render_to_string(template_name, { 80 | "notification": self, 81 | "target_object": self.get_target_object() 82 | }) 83 | 84 | 85 | @receiver(reported_as_fallacy) 86 | def create_fallacy_notification(sender, report, *args, **kwargs): 87 | Notification.objects.create( 88 | sender=None, # notification should be anonymous 89 | recipient=report.premise.user, 90 | notification_type=NOTIFICATION_REPORTED_AS_FALLACY, 91 | target_object_id=report.id 92 | ) 93 | 94 | 95 | @receiver(added_premise_for_premise) 96 | def create_premise_answer_notification(sender, premise, *args, **kwargs): 97 | if premise.user != premise.parent.user: 98 | Notification.objects.create( 99 | sender=premise.user, 100 | recipient=premise.parent.user, 101 | notification_type=NOTIFICATION_ADDED_PREMISE_FOR_PREMISE, 102 | target_object_id=premise.id 103 | ) 104 | 105 | 106 | @receiver(supported_a_premise) 107 | def create_premise_answer_notification(premise, user, *args, **kwargs): 108 | Notification.objects.create( 109 | sender=user, 110 | recipient=premise.user, 111 | notification_type=NOTIFICATION_SUPPORTED_A_PREMISE, 112 | target_object_id=premise.id 113 | ) 114 | 115 | 116 | @receiver(added_premise_for_contention) 117 | def create_contention_contribution_notification(sender, premise, *args, **kwargs): 118 | if premise.user != premise.argument.user: 119 | Notification.objects.create( 120 | sender=premise.user, 121 | recipient=premise.argument.user, 122 | notification_type=NOTIFICATION_ADDED_PREMISE_FOR_CONTENTION, 123 | target_object_id=premise.id 124 | ) 125 | 126 | 127 | @receiver(follow_done) 128 | def create_following_notification(following, follower, **kwargs): 129 | """ 130 | Sends notification to the followed user from the follower. 131 | """ 132 | Notification.objects.create( 133 | target_object_id=follower.id, 134 | notification_type=NOTIFICATION_FOLLOWED_A_PROFILE, 135 | sender=follower, 136 | recipient_id=following.id 137 | ) 138 | -------------------------------------------------------------------------------- /web/templates/tos.md: -------------------------------------------------------------------------------- 1 | ## Kullanım Koşulları 2 | 3 | Arguman.org'u ziyaretiniz ve arguman.org içeriğini kullanımınız aşağıdaki kullanım şartlarına tabidir: 4 | 5 | ### Uygunsuz İçerik 6 | 7 | arguman.org içeriği önceden kontrol edilmiyor olması nedeniyle 18 yaşından küçüklere uygun olmayabilir. çocuklarınızın gelişimini olumsuz etkileyecek içeriklerden uzak durmasını sağlayabileceğiniz filtre yazılımları bulunmaktadır, bunları kullanmanızı tavsiye ederiz. internet'in çocuklarca güvenli kullanımı konusunda bilgilendirme için: (http://www.guvenliweb.org.tr/annebabakilavuz/) 8 | 9 | ### Hukuka aykırı içerik ve şikayet 10 | 11 | arguman.org(site), arguman.org'a ait olup, 5651 sayılı kanun çerçevesinde yer sağlayıcı sıfatı’yla(yer sağlayıcı belge no:XXXX t:XX/XX/20XX) hizmet vermektedir. kullanıcılar tarafından oluşturulan içerikler herhangi bir ön incelemeye tabi olmaksızın ve doğrudan kullanıcılar tarafından yayına alınmaktadır. tarafımıza başvurulmadığı müddetçe, yayınlanan içeriğin hukuka uygunluğunu denetleme yükümlülüğümüz bulunmamaktadır. ancak, arguman.org yer sağladığı içeriğin hukuka uygunluğunu sağlamaya özen göstermekte, bu nedenle yapılan her başvuruyu dikkatle değerlendirmektedir. 12 | 13 | bir argümanda yer alan önermeler genelde farklı kullanıcılara ait olmaktadır. bu nedenle, başlıklarda yer alan her bir yazı 'şikayet' butonu kullanılarak ayrı ayrı şikayet edilebilmektedir. bunun haricinde, sitede yer alan iletişim arabiriminden bize ulaşabilirsiniz. 14 | 15 | şikayetleriniz kişisel haberleşme niteliğinde olmayıp, şikayetiniz ve iletişim bilgileriniz gerekli görüldüğü takdirde yayınlanabilir, üçüncü kişilerle ve/veya yasal mercilerle paylaşılabilir. bu nedenle şikayetlerinizde ifşa edilmesini istemediğiniz beyanlarda bulunmamanızı tavsiye ederiz. 16 | 17 | ## Kullanım 18 | 19 | sitede yer alan hizmetler ve içeriklerden şahsi kullanımınız için faydalanmanız gerekmektedir. site tarafından sunulan hizmetlerin ve arguman.org’tan yazılı izin alınmadığı müddetçe ticari amaçla kullanılması yasaktır. 20 | 21 | sitemize çeşitli yazılımlar veya aletler kullanarak; izinsiz giriş yapma, veri değiştirme veya başka işlemlerle sitenin işleyişine müdahale etme veya engelleme, sitenin işleyişini kasıtlı olarak yavaşlatma, virüs vs. zararlı içerik yüklemek suretiyle arguman.org’e zarar verme girişiminde bulunduğunuz takdirde arguman.org’un yasal yollara başvurma hakkı saklıdır. 22 | 23 | 24 | # İçerik 25 | 26 | arguman.org’te yer alan içeriğin doğru ve/veya güncel olduğu hiçbir şekilde iddia veya garanti edilmemektedir. aksine, kullanıcılar tamamen gerçekdışı içerik dahi üretebilirler. arguman.org’te yer alan içeriğe yukarıda 'hukuka aykırı içerik ve şikayet' kısmında belirtilen haller dışında müdahale edilmediğinden, arguman.org’te yer alan herhangi bir bilgi, yorum, öneri, tecrübe paylaşımı ve benzeri içeriğin referans alınması nedeniyle oluşabilecek (dolaylı veya doğrudan)maddi ve/veya manevi herhangi bir kaybınızdan sorumlu olmadığımızı belirtmek isteriz. 27 | 28 | arguman.org’te yer alan bağlantılara/yönlendirmelere (link) ilişkin hiçbir sorumluluğumuz bulunmamaktadır. arguman.org bağlantı ve yönlendirmelerin içeriğini kontrol etmediği gibi, içeriğin doğruluğu, yönlendirilen adreste virüs, trojan, phishing ve benzeri zararlı bir unsur olmadığı veya yönlendirilen sitenin hukuka uygun olduğu gibi veya benzeri hiçbir bir garanti vermemektedir. 29 | 30 | ## Telif hakları ve alıntı 31 | 32 | arguman.org bir derleme eser olup, derleme esere (içerik) ve site kodlarına ilişkin tüm haklar(kopyalama, çoğaltma, işleme, yayma) arguman.org’ye aittir. içeriğe ilişkin kullanıcılar ile arguman.org arasında yapılmış sözleşme hükümleri saklıdır. 33 | 34 | arguman.org içeriğini, içeriği değiştirmemek, ilgili kullanıcıya ve arguman.org’e aktif link vererek atıfta bulunmak ve ticari amaç gütmeden kullanmak kaydıyla alıntılamanız mümkündür ancak tüm bu kurallara uyulsa dahi alıntılama eserin, konularının veya başlıklarının tümünü kapsamayacak ve tam içeriğe ulaşmak için arguman.org'ün ziyaret edilmesi ihtiyacını ortadan kaldırmayacak düzeyde olmalıdır. 35 | 36 | aksi yönde açık yazılı izin olmadığı müddetçe site içeriğinin kısmen veya tamamen ticari amaçla ve/veya reklam ve benzeri gelir elde edecek şekilde kullanılması yasaktır. 37 | 38 | arguman.org’nin dilediği zaman, dilediği kişi veya kurumun yukarıdaki kurallara uyarak dahi alıntı yapmasını engelleme hakkı saklıdır. 39 | 40 | ## Gizlilik 41 | 42 | arguman.org’te bulunduğunuz süre boyunca 'cookie' olarak da adlandırılan çerezlerin ve buna benzer unsurların bilgisayarınıza yerleştirilmesi söz konusu olabilir. çerezler basit metin dosyalarından ibaret olup, kimlik ve sair özel bilgiler içermez, bu nevi kişisel bilgi içermemekle beraber, oturum bilgileri ve benzeri veriler saklanır ve sizi tekrar tanımak ve benzeri hizmetler için kullanılabilir. bu konuda daha fazla bilgiyi (http://www.allaboutcookies.org/) (http://en.wikipedia.org/wiki/http_cookie) ve http://tr.wikipedia.org/wiki/çerez_(internet) adreslerinden edinebilirsiniz. (verilen linklerden ulaşacağınız içeriğin güvenilirliğine ilişkin sorumluluğumuz bulunmamaktadır.) 43 | 44 | arguman.org'ü ziyaretiniz esnasında ip adresiniz ve bilgisayarınız aracılığıyla toplanabilen diğer veriler arguman.org tarafından anonim olarak kaydedilmektedir. 45 | 46 | ## arguman.org’un hak ve yükümlülükleri 47 | 48 | arguman.org kullanım koşullarını önceden bildirmeksizin değiştirebilir. bu nedenle kullanım koşullarını belirli aralıklarla incelemeniz öneririz. 49 | 50 | arguman.org, arguman.org'u oluşturan tüm unsurları önceden haber vermeksizin değiştirme, sona erdirme, yenilerini ekleme, ücretlendirme haklarını saklı tutmaktadır. 51 | 52 | arguman.org, gerekli gördüğü takdirde belli kişilerin, kurumların, ip numaralarının veya ip bloklarının arguman.org'e erişimini geçici veya kalıcı olarak durdurma hakkını saklı tutar. 53 | 54 | sitenin virüs ve sair zararlı içerik barındırmaması için özen sarf edilmekle birlikte, gelişen teknoloji, teknik sorun ve diğer nedenlerle bilgisayarınıza virüs, trojan gibi zararlı yazılımların bulaşması ihtimali bulunmaktadır. bu gibi risklere karşı antivirüs programları ve benzeri uygulamalar kullanmanızı tavsiye ederiz. sitemizde gezinmeniz dolayısıyla karşılaşabileceğiniz yazılımsal ve donanımsal dahil olmak üzere hiçbir zarara ilişkin sorumluluk kabul etmemekteyiz. bu nedenlerle sitemizden talepte bulunmayacağınızı peşinen kabul etmektesiniz. 55 | 56 | bu belge son olarak 26.10.2014 tarihinde güncellenmiştir. -------------------------------------------------------------------------------- /web/newsfeed/models.py: -------------------------------------------------------------------------------- 1 | from bson import ObjectId 2 | from datetime import datetime 3 | from django.db.models.signals import post_delete, post_save 4 | 5 | from django.dispatch import receiver 6 | from django.template.loader import render_to_string 7 | 8 | from newsfeed.utils import get_collection 9 | from premises.models import Contention, Premise, Report 10 | from premises.signals import reported_as_fallacy, added_premise_for_premise, added_premise_for_contention 11 | from profiles.signals import follow_done, unfollow_done 12 | from newsfeed.constants import * 13 | 14 | RELATED_MODELS = { 15 | NEWS_TYPE_CONTENTION: Contention, 16 | NEWS_TYPE_PREMISE: Premise, 17 | NEWS_TYPE_FALLACY: Report 18 | } 19 | 20 | 21 | class EntryManager(object): 22 | """ 23 | A manager that allows you to manage newsfeed items. 24 | """ 25 | 26 | def __init__(self): 27 | self.load() 28 | 29 | def load(self): 30 | self.collection = get_collection("newsfeed") 31 | 32 | def create(self, object_id, news_type, sender, recipients=None, 33 | related_object=None, date_creation=None): 34 | """ 35 | Creates newsfeed item from provided parameters 36 | """ 37 | 38 | followers = sender.followers.values_list("id", flat=True) 39 | recipients = (recipients if recipients is not None 40 | else list(followers) + [sender.pk]) 41 | 42 | entry_bundle = { 43 | "object_id": object_id, 44 | "news_type": news_type, 45 | "date_created": date_creation or datetime.now(), 46 | "sender": { 47 | "username": sender.username, 48 | "email": sender.email # it's required for gravatar 49 | }, 50 | "recipients": recipients 51 | } 52 | 53 | # sometimes we have to create custom related object bundle. 54 | # for example: following actions. because user actions are 55 | # holding on relational database. 56 | if related_object is not None: 57 | entry_bundle["related_object"] = related_object 58 | 59 | self.collection.insert(entry_bundle) 60 | 61 | def add_to_recipients(self, following, follower): 62 | """ 63 | Adds the id of follower to the recipients of followed profile's entries. 64 | """ 65 | self.collection.update( 66 | {"sender.username": following.username}, 67 | {"$push": {"recipients": follower.id}}, multi=True) 68 | 69 | def remove_from_recipients(self, following, follower): 70 | """ 71 | Removes follower id from the recipients of followed profile's entries. 72 | """ 73 | self.collection.update( 74 | {"sender.username": following.username}, 75 | {"$pull": {"recipients": follower.id}}, multi=True) 76 | 77 | def delete(self, object_type, object_id): 78 | """ 79 | Removes news entry from provided object type and object id. 80 | """ 81 | self.collection.remove({ 82 | "news_type": object_type, 83 | "object_id": object_id}) 84 | 85 | 86 | class Entry(dict): 87 | """ 88 | A model that wraps mongodb document for newsfeed. 89 | """ 90 | objects = EntryManager() 91 | 92 | def render(self): 93 | return render_to_string(self.get_template(), { 94 | "entry": self, 95 | "related_object": self.related_object 96 | }) 97 | 98 | __getattr__ = dict.get 99 | 100 | def get_template(self): 101 | return { 102 | NEWS_TYPE_CONTENTION: "newsfeed/contention.html", 103 | NEWS_TYPE_PREMISE: "newsfeed/premise.html", 104 | NEWS_TYPE_FALLACY: "newsfeed/fallacy.html", 105 | NEWS_TYPE_FOLLOWING: "newsfeed/following.html", 106 | }.get(self.news_type) 107 | 108 | def entry_class(self): 109 | return { 110 | NEWS_TYPE_CONTENTION: "contention_entry", 111 | NEWS_TYPE_PREMISE: "premise_entry", 112 | NEWS_TYPE_FALLACY: "fallacy_entry", 113 | NEWS_TYPE_FOLLOWING: "following_entry", 114 | }.get(self.news_type) 115 | 116 | 117 | @receiver(post_save, sender=Contention) 118 | def create_contention_entry(instance, created, **kwargs): 119 | """ 120 | Creates news entries for contentions. 121 | """ 122 | if created: 123 | Entry.objects.create( 124 | object_id=instance.id, 125 | news_type=instance.get_newsfeed_type(), 126 | sender=instance.get_actor(), 127 | related_object=instance.get_newsfeed_bundle() 128 | ) 129 | 130 | 131 | @receiver(added_premise_for_contention) 132 | @receiver(added_premise_for_premise) 133 | def create_premise_entry(premise, **kwargs): 134 | """ 135 | Creates news entries for the following types: 136 | - Premise 137 | - Report 138 | That models have `get_news_type` method. 139 | """ 140 | Entry.objects.create( 141 | object_id=premise.id, 142 | news_type=premise.get_newsfeed_type(), 143 | sender=premise.get_actor(), 144 | related_object=premise.get_newsfeed_bundle() 145 | ) 146 | 147 | 148 | @receiver(reported_as_fallacy) 149 | def create_fallacy_entry(report, **kwargs): 150 | """ 151 | Creates fallacy entries. 152 | """ 153 | Entry.objects.create( 154 | object_id=report.id, 155 | news_type=report.get_newsfeed_type(), 156 | sender=report.get_actor(), 157 | related_object=report.get_newsfeed_bundle() 158 | ) 159 | 160 | 161 | @receiver(follow_done) 162 | def create_following_entry(follower, following, **kwargs): 163 | """ 164 | Creates news entry for following actions. 165 | """ 166 | Entry.objects.create( 167 | object_id=following.id, 168 | news_type=NEWS_TYPE_FOLLOWING, 169 | sender=follower, 170 | related_object=dict(username=following.username, 171 | email=following.email) 172 | ) 173 | 174 | 175 | @receiver(follow_done) 176 | def add_to_recipients(follower, following, **kwargs): 177 | """ 178 | Adds the entries of followed profile to follower's newsfeed. 179 | """ 180 | Entry.objects.add_to_recipients( 181 | following=following, follower=follower) 182 | 183 | 184 | @receiver(unfollow_done) 185 | def remove_from_recipients(follower, following, **kwargs): 186 | """ 187 | Removes the entries of unfollowed profile. 188 | """ 189 | Entry.objects.remove_from_recipients(following=following, 190 | follower=follower) 191 | 192 | 193 | @receiver(post_delete, sender=Contention) 194 | @receiver(post_delete, sender=Premise) 195 | def remove_news_entry(instance, **kwargs): 196 | Entry.objects.delete( 197 | object_type=instance.get_newsfeed_type(), 198 | object_id=instance.id 199 | ) 200 | -------------------------------------------------------------------------------- /web/static/css/h5bp.css: -------------------------------------------------------------------------------- 1 | /*! HTML5 Boilerplate v4.3.0 | MIT License | http://h5bp.com/ */ 2 | 3 | /* 4 | * What follows is the result of much research on cross-browser styling. 5 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, 6 | * Kroc Camen, and the H5BP dev community and team. 7 | */ 8 | 9 | /* ========================================================================== 10 | Base styles: opinionated defaults 11 | ========================================================================== */ 12 | 13 | html, 14 | button, 15 | input, 16 | select, 17 | textarea { 18 | color: #222; 19 | } 20 | 21 | html { 22 | font-size: 1em; 23 | line-height: 1.4; 24 | } 25 | 26 | /* 27 | * Remove text-shadow in selection highlight: h5bp.com/i 28 | * These selection rule sets have to be separate. 29 | * Customize the background color to match your design. 30 | */ 31 | 32 | ::-moz-selection { 33 | background: #b3d4fc; 34 | text-shadow: none; 35 | } 36 | 37 | ::selection { 38 | background: #b3d4fc; 39 | text-shadow: none; 40 | } 41 | 42 | /* 43 | * A better looking default horizontal rule 44 | */ 45 | 46 | hr { 47 | display: block; 48 | height: 1px; 49 | border: 0; 50 | border-top: 1px solid #ccc; 51 | margin: 1em 0; 52 | padding: 0; 53 | } 54 | 55 | /* 56 | * Remove the gap between images, videos, audio and canvas and the bottom of 57 | * their containers: h5bp.com/i/440 58 | */ 59 | 60 | audio, 61 | canvas, 62 | img, 63 | video { 64 | vertical-align: middle; 65 | } 66 | 67 | /* 68 | * Remove default fieldset styles. 69 | */ 70 | 71 | fieldset { 72 | border: 0; 73 | margin: 0; 74 | padding: 0; 75 | } 76 | 77 | /* 78 | * Allow only vertical resizing of textareas. 79 | */ 80 | 81 | textarea { 82 | resize: vertical; 83 | } 84 | 85 | /* ========================================================================== 86 | Browse Happy prompt 87 | ========================================================================== */ 88 | 89 | .browsehappy { 90 | margin: 0.2em 0; 91 | background: #ccc; 92 | color: #000; 93 | padding: 0.2em 0; 94 | } 95 | 96 | /* ========================================================================== 97 | Author's custom styles 98 | ========================================================================== */ 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | /* ========================================================================== 117 | Helper classes 118 | ========================================================================== */ 119 | 120 | /* 121 | * Image replacement 122 | */ 123 | 124 | .ir { 125 | background-color: transparent; 126 | border: 0; 127 | overflow: hidden; 128 | /* IE 6/7 fallback */ 129 | *text-indent: -9999px; 130 | } 131 | 132 | .ir:before { 133 | content: ""; 134 | display: block; 135 | width: 0; 136 | height: 150%; 137 | } 138 | 139 | /* 140 | * Hide from both screenreaders and browsers: h5bp.com/u 141 | */ 142 | 143 | .hidden { 144 | display: none !important; 145 | visibility: hidden; 146 | } 147 | 148 | /* 149 | * Hide only visually, but have it available for screenreaders: h5bp.com/v 150 | */ 151 | 152 | .visuallyhidden { 153 | border: 0; 154 | clip: rect(0 0 0 0); 155 | height: 1px; 156 | margin: -1px; 157 | overflow: hidden; 158 | padding: 0; 159 | position: absolute; 160 | width: 1px; 161 | } 162 | 163 | /* 164 | * Extends the .visuallyhidden class to allow the element to be focusable 165 | * when navigated to via the keyboard: h5bp.com/p 166 | */ 167 | 168 | .visuallyhidden.focusable:active, 169 | .visuallyhidden.focusable:focus { 170 | clip: auto; 171 | height: auto; 172 | margin: 0; 173 | overflow: visible; 174 | position: static; 175 | width: auto; 176 | } 177 | 178 | /* 179 | * Hide visually and from screenreaders, but maintain layout 180 | */ 181 | 182 | .invisible { 183 | visibility: hidden; 184 | } 185 | 186 | /* 187 | * Clearfix: contain floats 188 | * 189 | * For modern browsers 190 | * 1. The space content is one way to avoid an Opera bug when the 191 | * `contenteditable` attribute is included anywhere else in the document. 192 | * Otherwise it causes space to appear at the top and bottom of elements 193 | * that receive the `clearfix` class. 194 | * 2. The use of `table` rather than `block` is only necessary if using 195 | * `:before` to contain the top-margins of child elements. 196 | */ 197 | 198 | .clearfix:before, 199 | .clearfix:after { 200 | content: " "; /* 1 */ 201 | display: table; /* 2 */ 202 | } 203 | 204 | .clearfix:after { 205 | clear: both; 206 | } 207 | 208 | /* 209 | * For IE 6/7 only 210 | * Include this rule to trigger hasLayout and contain floats. 211 | */ 212 | 213 | .clearfix { 214 | *zoom: 1; 215 | } 216 | 217 | /* ========================================================================== 218 | EXAMPLE Media Queries for Responsive Design. 219 | These examples override the primary ('mobile first') styles. 220 | Modify as content requires. 221 | ========================================================================== */ 222 | 223 | @media only screen and (min-width: 35em) { 224 | /* Style adjustments for viewports that meet the condition */ 225 | } 226 | 227 | @media print, 228 | (-o-min-device-pixel-ratio: 5/4), 229 | (-webkit-min-device-pixel-ratio: 1.25), 230 | (min-resolution: 120dpi) { 231 | /* Style adjustments for high resolution devices */ 232 | } 233 | 234 | /* ========================================================================== 235 | Print styles. 236 | Inlined to avoid required HTTP connection: h5bp.com/r 237 | ========================================================================== */ 238 | 239 | @media print { 240 | * { 241 | background: transparent !important; 242 | color: #000 !important; /* Black prints faster: h5bp.com/s */ 243 | box-shadow: none !important; 244 | text-shadow: none !important; 245 | } 246 | 247 | a, 248 | a:visited { 249 | text-decoration: underline; 250 | } 251 | 252 | a[href]:after { 253 | content: " (" attr(href) ")"; 254 | } 255 | 256 | abbr[title]:after { 257 | content: " (" attr(title) ")"; 258 | } 259 | 260 | /* 261 | * Don't show links for images, or javascript/internal links 262 | */ 263 | 264 | .ir a:after, 265 | a[href^="javascript:"]:after, 266 | a[href^="#"]:after { 267 | content: ""; 268 | } 269 | 270 | pre, 271 | blockquote { 272 | border: 1px solid #999; 273 | page-break-inside: avoid; 274 | } 275 | 276 | thead { 277 | display: table-header-group; /* h5bp.com/t */ 278 | } 279 | 280 | tr, 281 | img { 282 | page-break-inside: avoid; 283 | } 284 | 285 | img { 286 | max-width: 100% !important; 287 | } 288 | 289 | @page { 290 | margin: 0.5cm; 291 | } 292 | 293 | p, 294 | h2, 295 | h3 { 296 | orphans: 3; 297 | widows: 3; 298 | } 299 | 300 | h2, 301 | h3 { 302 | page-break-after: avoid; 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /web/static/js/main.js: -------------------------------------------------------------------------------- 1 | (function (arguman) { 2 | 3 | arguman.utils = { 4 | adder: function (a, b) { 5 | return a + b 6 | } 7 | }; 8 | 9 | arguman.KeyboardManager = Class.extend({ 10 | currentElement: null, 11 | init: function (options) { 12 | $.extend(this, options); 13 | this.$el = $(this.el); 14 | this.bindEvents(); 15 | this.setInitial(); 16 | }, 17 | up: function () { 18 | this.select( 19 | this.currentElement.parent().parent(), 20 | true 21 | ) 22 | }, 23 | down: function () { 24 | this.select( 25 | this.currentElement.find("ul").find(".child-premise").first(), 26 | true 27 | ) 28 | }, 29 | left: function () { 30 | this.select( 31 | this.currentElement.prev(), 32 | true 33 | ); 34 | }, 35 | right: function () { 36 | this.select( 37 | this.currentElement.next(), 38 | true 39 | ); 40 | }, 41 | select: function (leaf, scroll) { 42 | if (leaf.is(".child-premise")) { 43 | this.$el.find(".premise").removeClass("focused"); 44 | this.currentElement = leaf; 45 | leaf.find(".premise").first().addClass("focused"); 46 | if (scroll) { 47 | this.scrollTo(leaf); 48 | } 49 | } 50 | }, 51 | needsScroll: function () { 52 | var maxHeight = Math.max.apply(this, 53 | this.$el.find(".premise") 54 | .toArray() 55 | .map(function (el) { 56 | return $(el).offset().top + $(el).height(); 57 | })); 58 | 59 | return (this.$el.width() > window.innerWidth || 60 | maxHeight > window.innerHeight) 61 | }, 62 | scrollTo: function (el) { 63 | if (this.needsScroll()) { 64 | var center = el.offset().left + (el.width()/2); 65 | $('html, body').animate({ 66 | scrollTop: el.offset().top - 200, 67 | scrollLeft: center - (window.innerWidth / 2) 68 | }, 150); 69 | } 70 | }, 71 | setInitial: function () { 72 | if (this.needsScroll()) { 73 | this.select(this.$el.find(".child-premise").first(), false); 74 | } 75 | }, 76 | bindEvents: function () { 77 | $(document).keydown(function(e) { 78 | switch(e.which) { 79 | case 37: 80 | this.left(); 81 | break; 82 | 83 | case 38: 84 | this.up(); 85 | break; 86 | 87 | case 39: 88 | this.right(); 89 | break; 90 | 91 | case 40: 92 | this.down(); 93 | break; 94 | 95 | default: return; // exit this handler for other keys 96 | } 97 | e.preventDefault(); // prevent the default action (scroll / move caret) 98 | }.bind(this)); 99 | 100 | if (this.needsScroll()) { 101 | this.$el.find(".premise-content").on('click', function (event) { 102 | this.select($(event.target).parents(".child-premise").eq(0)) 103 | }.bind(this)); 104 | $(this.info).show(); 105 | $(window).on("scroll", function () { 106 | $(this.info).fadeOut(100); 107 | }.bind(this)); 108 | } else { 109 | $(this.info).hide(); 110 | } 111 | 112 | } 113 | }); 114 | 115 | arguman.CollapsibleTree = Class.extend({ 116 | 117 | premiseWidth: 260, 118 | 119 | init: function (options) { 120 | $.extend(this, options); 121 | this.$el = $(this.el); 122 | }, 123 | 124 | setTreeWidth: function () { 125 | /* 126 | * Set full width to container, and reduce the width with 127 | * positions of last premise. 128 | * */ 129 | 130 | if (this.$el.hasClass("empty")) { 131 | return; 132 | } 133 | 134 | var root = this.$el.find(".root"), 135 | mainContention = $(this.mainContention); 136 | var treeWidth = parseInt(this.$el.data("width")) * (this.premiseWidth * 2); 137 | this.width = treeWidth; 138 | this.$el.width(treeWidth); 139 | 140 | var mainPremises = root.next().children(); 141 | 142 | if (mainPremises.length) { 143 | var premises = root.parent().find("li"); 144 | 145 | var maxPosition = Math.max.apply(this, 146 | premises.toArray().map(function (premise) { 147 | return $(premise).offset().left 148 | })); 149 | 150 | this.width = (maxPosition + this.premiseWidth + 50); 151 | this.$el.width(this.width); 152 | mainContention.css({ 153 | "margin-left": (root.position().left) - (mainContention.width() / 2) 154 | }); 155 | } 156 | 157 | if (this.width < window.innerWidth) { 158 | this.$el.css({ 159 | "margin-left": (window.innerWidth / 2) - (this.width / 2) 160 | }); 161 | mainContention.css({ 162 | "margin-left": (window.innerWidth / 2) - (mainContention.width() / 2) 163 | }); 164 | } 165 | 166 | }, 167 | 168 | render: function () { 169 | this.setTreeWidth(); 170 | this.$el.css("visibility", "visible"); 171 | } 172 | }); 173 | 174 | arguman.Zoom = Class.extend({ 175 | canvas: '#app', 176 | currentSize: function () { 177 | return parseFloat($(this.canvas).css('zoom')) || 1 178 | }, 179 | zoomOut: function () { 180 | var current = this.currentSize(); 181 | $(this.canvas).css('zoom', current - 0.1); 182 | $('#zoomIn').show(); 183 | $(this.canvas).css('padding-top', function (index, curValue) { 184 | return parseInt(curValue, 10) + 40 + 'px'; 185 | }); 186 | }, 187 | zoomIn: function () { 188 | var current = this.currentSize(); 189 | $('#app').css('zoom', current + 0.1); 190 | if (parseFloat($(this.canvas).css('zoom')) >= 1) { 191 | $('#zoomIn').hide(); 192 | } 193 | $(this.canvas).css('padding-top', function (index, curValue) { 194 | return parseInt(curValue, 10) - 40 + 'px'; 195 | }); 196 | }, 197 | init: function () { 198 | $('#zoomIn').on('click', $.proxy(this, 'zoomIn')); 199 | $('#zoomOut').on('click', $.proxy(this, 'zoomOut')); 200 | } 201 | }); 202 | 203 | })(window.arguman || (window.arguman = {})); -------------------------------------------------------------------------------- /web/premises/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import operator 4 | import os 5 | 6 | from uuid import uuid4 7 | from django.utils.html import escape 8 | from markdown2 import markdown 9 | from unidecode import unidecode 10 | 11 | from django.core import validators 12 | from django.conf import settings 13 | from django.template.loader import render_to_string 14 | from django.db import models 15 | from django.template.defaultfilters import slugify 16 | from django.utils.encoding import smart_unicode 17 | from django.utils.functional import curry 18 | from newsfeed.constants import NEWS_TYPE_FALLACY, NEWS_TYPE_PREMISE, NEWS_TYPE_CONTENTION 19 | from premises.constants import MAX_PREMISE_CONTENT_LENGTH 20 | 21 | from premises.managers import ContentionManager, DeletePreventionManager 22 | from premises.mixins import DeletePreventionMixin 23 | 24 | OBJECTION = 0 25 | SUPPORT = 1 26 | SITUATION = 2 27 | 28 | PREMISE_TYPES = ( 29 | (OBJECTION, u"ama"), 30 | (SUPPORT, u"çünkü"), 31 | (SITUATION, u"ancak"), 32 | ) 33 | 34 | 35 | class Channel(models.Model): 36 | title = models.CharField(max_length=255) 37 | 38 | def __unicode__(self): 39 | return smart_unicode(self.title) 40 | 41 | 42 | class Contention(DeletePreventionMixin, models.Model): 43 | channel = models.ForeignKey(Channel, related_name='contentions', 44 | null=True, blank=True) 45 | title = models.CharField( 46 | max_length=255, verbose_name="Argüman", 47 | help_text=render_to_string("premises/examples/contention.html")) 48 | slug = models.SlugField(max_length=255, blank=True) 49 | description = models.TextField( 50 | null=True, blank=True, verbose_name="Ek bilgiler",) 51 | user = models.ForeignKey(settings.AUTH_USER_MODEL) 52 | owner = models.CharField( 53 | max_length=255, null=True, blank=True, 54 | verbose_name="Orijinal söylem", 55 | help_text=render_to_string("premises/examples/owner.html")) 56 | sources = models.TextField( 57 | null=True, blank=True, 58 | verbose_name="Kaynaklar", 59 | help_text=render_to_string("premises/examples/sources.html")) 60 | is_featured = models.BooleanField(default=False) 61 | is_published = models.BooleanField(default=True) 62 | date_creation = models.DateTimeField(auto_now_add=True) 63 | date_modification = models.DateTimeField(auto_now_add=True, 64 | auto_now=True) 65 | ip_address = models.IPAddressField(null=True, blank=True) 66 | 67 | objects = ContentionManager() 68 | 69 | class Meta: 70 | ordering = ["-date_creation"] 71 | 72 | def __unicode__(self): 73 | return smart_unicode(self.title) 74 | 75 | @models.permalink 76 | def get_absolute_url(self): 77 | return 'contention_detail', [self.slug] 78 | 79 | def get_full_url(self): 80 | return "http://%(site_url)s%(path)s" % { 81 | "site_url": settings.SITE_URL, 82 | "path": self.get_absolute_url() 83 | } 84 | 85 | def save(self, *args, **kwargs): 86 | """ 87 | - Make unique slug if it is not given. 88 | """ 89 | if not self.slug: 90 | slug = slugify(unidecode(self.title)) 91 | duplications = Contention.objects.filter(slug=slug) 92 | if duplications.exists(): 93 | self.slug = "%s-%s" % (slug, uuid4().hex) 94 | else: 95 | self.slug = slug 96 | return super(Contention, self).save(*args, **kwargs) 97 | 98 | def published_premises(self, parent=None, ignore_parent=False): 99 | premises = self.premises.filter(is_approved=True) 100 | if ignore_parent: 101 | return premises 102 | return premises.filter(parent=parent) 103 | 104 | published_children = published_premises 105 | 106 | def children_by_premise_type(self, premise_type=None, ignore_parent=False): 107 | return (self.published_premises(ignore_parent=ignore_parent) 108 | .filter(premise_type=premise_type)) 109 | 110 | because = curry(children_by_premise_type, 111 | premise_type=SUPPORT, ignore_parent=True) 112 | but = curry(children_by_premise_type, 113 | premise_type=OBJECTION, ignore_parent=True) 114 | however = curry(children_by_premise_type, 115 | premise_type=SITUATION, ignore_parent=True) 116 | 117 | def update_sibling_counts(self): 118 | for premise in self.premises.filter(): 119 | premise.update_sibling_counts() 120 | 121 | def last_user(self): 122 | try: 123 | # add date_creation 124 | premise = self.premises.order_by("-pk")[0] 125 | except IndexError: 126 | user = self.user 127 | else: 128 | user = premise.user 129 | return user 130 | 131 | def width(self): 132 | children = self.published_children() 133 | return children.count() + reduce(operator.add, 134 | map(operator.methodcaller("width"), 135 | children), 0) 136 | 137 | def get_actor(self): 138 | """ 139 | Encapsulated for newsfeed app. 140 | """ 141 | return self.user 142 | 143 | def get_newsfeed_type(self): 144 | return NEWS_TYPE_CONTENTION 145 | 146 | def get_newsfeed_bundle(self): 147 | return { 148 | "title": self.title, 149 | "owner": self.owner, 150 | "uri": self.get_absolute_url() 151 | } 152 | 153 | 154 | class Premise(DeletePreventionMixin, models.Model): 155 | argument = models.ForeignKey(Contention, related_name="premises") 156 | user = models.ForeignKey(settings.AUTH_USER_MODEL) 157 | parent = models.ForeignKey("self", related_name="children", 158 | null=True, blank=True, 159 | verbose_name="Öncülü", 160 | help_text="Önermenin öncülü. Eğer boş bırakılırsa" 161 | "ana argümanın bir önermesi olur.") 162 | premise_type = models.IntegerField( 163 | default=SUPPORT, 164 | choices=PREMISE_TYPES, verbose_name="Önerme Tipi", 165 | help_text=render_to_string("premises/examples/premise_type.html")) 166 | text = models.TextField( 167 | null=True, blank=True, 168 | verbose_name="Önermenin İçeriği", 169 | help_text=render_to_string("premises/examples/premise.html"), 170 | validators=[validators.MaxLengthValidator(MAX_PREMISE_CONTENT_LENGTH)]) 171 | sources = models.TextField( 172 | null=True, blank=True, verbose_name="Kaynaklar", 173 | help_text=render_to_string("premises/examples/premise_source.html")) 174 | is_approved = models.BooleanField(default=True, verbose_name="Yayınla") 175 | collapsed = models.BooleanField(default=False) 176 | supporters = models.ManyToManyField(settings.AUTH_USER_MODEL, 177 | related_name="supporting") 178 | sibling_count = models.IntegerField(default=1) # denormalized field 179 | child_count = models.IntegerField(default=1) # denormalized field 180 | max_sibling_count = models.IntegerField(default=1) # denormalized field 181 | date_creation = models.DateTimeField(auto_now_add=True) 182 | ip_address = models.IPAddressField(null=True, blank=True) 183 | 184 | objects = DeletePreventionManager() 185 | 186 | def __unicode__(self): 187 | return smart_unicode(self.text) 188 | 189 | @models.permalink 190 | def get_absolute_url(self): 191 | return 'contention_detail', [self.argument.slug] 192 | 193 | def update_sibling_counts(self): 194 | count = self.get_siblings().count() 195 | self.get_siblings().update(sibling_count=count) 196 | 197 | def get_siblings(self): 198 | return Premise.objects.filter(parent=self.parent, 199 | argument=self.argument) 200 | 201 | def published_children(self): 202 | return self.children.filter(is_approved=True) 203 | 204 | def premise_class(self): 205 | return { 206 | OBJECTION: "but", 207 | SUPPORT: "because", 208 | SITUATION: "however" 209 | }.get(self.premise_type) 210 | 211 | def reported_by(self, user): 212 | return self.reports.filter(reporter=user).exists() 213 | 214 | def formatted_sources(self): 215 | return markdown(escape(self.sources), safe_mode=True) 216 | 217 | def formatted_text(self): 218 | return markdown(escape(self.text), safe_mode=True) 219 | 220 | def width(self): 221 | total = self.published_children().count() 222 | 223 | for child in self.published_children(): 224 | total += child.width() 225 | 226 | return total 227 | 228 | def fallacies(self): 229 | fallacies = set(self.reports.values_list("fallacy_type", flat=True)) 230 | mapping = dict(get_fallacy_types()) 231 | fallacy_list = [mapping.get(fallacy) for fallacy in fallacies] 232 | return filter(None, fallacy_list) 233 | 234 | def get_actor(self): 235 | # Encapsulated for newsfeed app. 236 | return self.user 237 | 238 | def get_newsfeed_type(self): 239 | return NEWS_TYPE_PREMISE 240 | 241 | def get_newsfeed_bundle(self): 242 | return { 243 | "premise_type": self.premise_type, 244 | "premise_class": self.premise_class(), 245 | "text": self.text, 246 | "sources": self.sources, 247 | "contention": self.argument.get_newsfeed_bundle() 248 | } 249 | 250 | def recent_supporters(self): 251 | return self.supporters.all()[0:5] 252 | 253 | class Comment(models.Model): 254 | premise = models.ForeignKey(Premise) 255 | user = models.ForeignKey(settings.AUTH_USER_MODEL) 256 | text = models.TextField() 257 | date_creation = models.DateTimeField(auto_now_add=True) 258 | is_active = models.BooleanField(default=True) 259 | 260 | def __unicode__(self): 261 | return smart_unicode(self.text) 262 | 263 | 264 | def get_fallacy_types(): 265 | if hasattr(get_fallacy_types, "cache"): 266 | return get_fallacy_types.cache 267 | 268 | get_fallacy_types.cache = json.load( 269 | open(os.path.join(os.path.dirname(__file__), 270 | "fallacies.json"))) 271 | 272 | return get_fallacy_types.cache 273 | 274 | 275 | class Report(models.Model): 276 | reporter = models.ForeignKey(settings.AUTH_USER_MODEL, 277 | related_name='reports') 278 | premise = models.ForeignKey(Premise, 279 | related_name='reports', 280 | blank=True, 281 | null=True) 282 | contention = models.ForeignKey(Contention, 283 | related_name='reports', 284 | blank=True, 285 | null=True) 286 | fallacy_type = models.CharField( 287 | "Safsata Tipi", choices=get_fallacy_types(), null=True, blank=False, 288 | max_length=255, default="Wrong Direction", 289 | help_text=render_to_string("premises/examples/fallacy.html")) 290 | 291 | 292 | def __unicode__(self): 293 | return smart_unicode(self.fallacy_type) 294 | 295 | def get_actor(self): 296 | """ 297 | Encapsulated for newsfeed app. 298 | """ 299 | return self.reporter 300 | 301 | def get_newsfeed_type(self): 302 | return NEWS_TYPE_FALLACY 303 | 304 | def get_newsfeed_bundle(self): 305 | return { 306 | "fallacy_type": self.fallacy_type, 307 | "premise": self.premise.get_newsfeed_bundle(), 308 | "contention": self.contention.get_newsfeed_bundle() 309 | } 310 | -------------------------------------------------------------------------------- /web/static/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.3 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | *display: inline; 35 | *zoom: 1; 36 | } 37 | 38 | /** 39 | * Prevent modern browsers from displaying `audio` without controls. 40 | * Remove excess height in iOS 5 devices. 41 | */ 42 | 43 | audio:not([controls]) { 44 | display: none; 45 | height: 0; 46 | } 47 | 48 | /** 49 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 50 | * Known issue: no IE 6 support. 51 | */ 52 | 53 | [hidden] { 54 | display: none; 55 | } 56 | 57 | /* ========================================================================== 58 | Base 59 | ========================================================================== */ 60 | 61 | /** 62 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 63 | * `em` units. 64 | * 2. Prevent iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | font-size: 100%; /* 1 */ 70 | -ms-text-size-adjust: 100%; /* 2 */ 71 | -webkit-text-size-adjust: 100%; /* 2 */ 72 | } 73 | 74 | /** 75 | * Address `font-family` inconsistency between `textarea` and other form 76 | * elements. 77 | */ 78 | 79 | html, 80 | button, 81 | input, 82 | select, 83 | textarea { 84 | font-family: sans-serif; 85 | } 86 | 87 | /** 88 | * Address margins handled incorrectly in IE 6/7. 89 | */ 90 | 91 | body { 92 | margin: 0; 93 | } 94 | 95 | /* ========================================================================== 96 | Links 97 | ========================================================================== */ 98 | 99 | /** 100 | * Address `outline` inconsistency between Chrome and other browsers. 101 | */ 102 | 103 | a:focus { 104 | outline: thin dotted; 105 | } 106 | 107 | /** 108 | * Improve readability when focused and also mouse hovered in all browsers. 109 | */ 110 | 111 | a:active, 112 | a:hover { 113 | outline: 0; 114 | } 115 | 116 | /* ========================================================================== 117 | Typography 118 | ========================================================================== */ 119 | 120 | /** 121 | * Address font sizes and margins set differently in IE 6/7. 122 | * Address font sizes within `section` and `article` in Firefox 4+, Safari 5, 123 | * and Chrome. 124 | */ 125 | 126 | h1 { 127 | font-size: 2em; 128 | margin: 0.67em 0; 129 | } 130 | 131 | h2 { 132 | font-size: 1.5em; 133 | margin: 0.83em 0; 134 | } 135 | 136 | h3 { 137 | font-size: 1.17em; 138 | margin: 1em 0; 139 | } 140 | 141 | h4 { 142 | font-size: 1em; 143 | margin: 1.33em 0; 144 | } 145 | 146 | h5 { 147 | font-size: 0.83em; 148 | margin: 1.67em 0; 149 | } 150 | 151 | h6 { 152 | font-size: 0.67em; 153 | margin: 2.33em 0; 154 | } 155 | 156 | /** 157 | * Address styling not present in IE 7/8/9, Safari 5, and Chrome. 158 | */ 159 | 160 | abbr[title] { 161 | border-bottom: 1px dotted; 162 | } 163 | 164 | /** 165 | * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. 166 | */ 167 | 168 | b, 169 | strong { 170 | font-weight: bold; 171 | } 172 | 173 | blockquote { 174 | margin: 1em 40px; 175 | } 176 | 177 | /** 178 | * Address styling not present in Safari 5 and Chrome. 179 | */ 180 | 181 | dfn { 182 | font-style: italic; 183 | } 184 | 185 | /** 186 | * Address differences between Firefox and other browsers. 187 | * Known issue: no IE 6/7 normalization. 188 | */ 189 | 190 | hr { 191 | -moz-box-sizing: content-box; 192 | box-sizing: content-box; 193 | height: 0; 194 | } 195 | 196 | /** 197 | * Address styling not present in IE 6/7/8/9. 198 | */ 199 | 200 | mark { 201 | background: #ff0; 202 | color: #000; 203 | } 204 | 205 | /** 206 | * Address margins set differently in IE 6/7. 207 | */ 208 | 209 | p, 210 | pre { 211 | margin: 1em 0; 212 | } 213 | 214 | /** 215 | * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. 216 | */ 217 | 218 | code, 219 | kbd, 220 | pre, 221 | samp { 222 | font-family: monospace, serif; 223 | _font-family: 'courier new', monospace; 224 | font-size: 1em; 225 | } 226 | 227 | /** 228 | * Improve readability of pre-formatted text in all browsers. 229 | */ 230 | 231 | pre { 232 | white-space: pre; 233 | white-space: pre-wrap; 234 | word-wrap: break-word; 235 | } 236 | 237 | /** 238 | * Address CSS quotes not supported in IE 6/7. 239 | */ 240 | 241 | q { 242 | quotes: none; 243 | } 244 | 245 | /** 246 | * Address `quotes` property not supported in Safari 4. 247 | */ 248 | 249 | q:before, 250 | q:after { 251 | content: ''; 252 | content: none; 253 | } 254 | 255 | /** 256 | * Address inconsistent and variable font size in all browsers. 257 | */ 258 | 259 | small { 260 | font-size: 80%; 261 | } 262 | 263 | /** 264 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 265 | */ 266 | 267 | sub, 268 | sup { 269 | font-size: 75%; 270 | line-height: 0; 271 | position: relative; 272 | vertical-align: baseline; 273 | } 274 | 275 | sup { 276 | top: -0.5em; 277 | } 278 | 279 | sub { 280 | bottom: -0.25em; 281 | } 282 | 283 | /* ========================================================================== 284 | Lists 285 | ========================================================================== */ 286 | 287 | /** 288 | * Address margins set differently in IE 6/7. 289 | */ 290 | 291 | dl, 292 | menu, 293 | ol, 294 | ul { 295 | margin: 1em 0; 296 | } 297 | 298 | dd { 299 | margin: 0 0 0 40px; 300 | } 301 | 302 | /** 303 | * Address paddings set differently in IE 6/7. 304 | */ 305 | 306 | menu, 307 | ol, 308 | ul { 309 | padding: 0 0 0 40px; 310 | } 311 | 312 | /** 313 | * Correct list images handled incorrectly in IE 7. 314 | */ 315 | 316 | nav ul, 317 | nav ol { 318 | list-style: none; 319 | list-style-image: none; 320 | } 321 | 322 | /* ========================================================================== 323 | Embedded content 324 | ========================================================================== */ 325 | 326 | /** 327 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 328 | * 2. Improve image quality when scaled in IE 7. 329 | */ 330 | 331 | img { 332 | border: 0; /* 1 */ 333 | -ms-interpolation-mode: bicubic; /* 2 */ 334 | } 335 | 336 | /** 337 | * Correct overflow displayed oddly in IE 9. 338 | */ 339 | 340 | svg:not(:root) { 341 | overflow: hidden; 342 | } 343 | 344 | /* ========================================================================== 345 | Figures 346 | ========================================================================== */ 347 | 348 | /** 349 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 350 | */ 351 | 352 | figure { 353 | margin: 0; 354 | } 355 | 356 | /* ========================================================================== 357 | Forms 358 | ========================================================================== */ 359 | 360 | /** 361 | * Correct margin displayed oddly in IE 6/7. 362 | */ 363 | 364 | form { 365 | margin: 0; 366 | } 367 | 368 | /** 369 | * Define consistent border, margin, and padding. 370 | */ 371 | 372 | fieldset { 373 | border: 1px solid #c0c0c0; 374 | margin: 0 2px; 375 | padding: 0.35em 0.625em 0.75em; 376 | } 377 | 378 | /** 379 | * 1. Correct color not being inherited in IE 6/7/8/9. 380 | * 2. Correct text not wrapping in Firefox 3. 381 | * 3. Correct alignment displayed oddly in IE 6/7. 382 | */ 383 | 384 | legend { 385 | border: 0; /* 1 */ 386 | padding: 0; 387 | white-space: normal; /* 2 */ 388 | *margin-left: -7px; /* 3 */ 389 | } 390 | 391 | /** 392 | * 1. Correct font size not being inherited in all browsers. 393 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 394 | * and Chrome. 395 | * 3. Improve appearance and consistency in all browsers. 396 | */ 397 | 398 | button, 399 | input, 400 | select, 401 | textarea { 402 | font-size: 100%; /* 1 */ 403 | margin: 0; /* 2 */ 404 | vertical-align: baseline; /* 3 */ 405 | *vertical-align: middle; /* 3 */ 406 | } 407 | 408 | /** 409 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 410 | * the UA stylesheet. 411 | */ 412 | 413 | button, 414 | input { 415 | line-height: normal; 416 | } 417 | 418 | /** 419 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 420 | * All other form control elements do not inherit `text-transform` values. 421 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 422 | * Correct `select` style inheritance in Firefox 4+ and Opera. 423 | */ 424 | 425 | button, 426 | select { 427 | text-transform: none; 428 | } 429 | 430 | /** 431 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 432 | * and `video` controls. 433 | * 2. Correct inability to style clickable `input` types in iOS. 434 | * 3. Improve usability and consistency of cursor style between image-type 435 | * `input` and others. 436 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 437 | * Known issue: inner spacing remains in IE 6. 438 | */ 439 | 440 | button, 441 | html input[type="button"], /* 1 */ 442 | input[type="reset"], 443 | input[type="submit"] { 444 | -webkit-appearance: button; /* 2 */ 445 | cursor: pointer; /* 3 */ 446 | *overflow: visible; /* 4 */ 447 | } 448 | 449 | /** 450 | * Re-set default cursor for disabled elements. 451 | */ 452 | 453 | button[disabled], 454 | html input[disabled] { 455 | cursor: default; 456 | } 457 | 458 | /** 459 | * 1. Address box sizing set to content-box in IE 8/9. 460 | * 2. Remove excess padding in IE 8/9. 461 | * 3. Remove excess padding in IE 7. 462 | * Known issue: excess padding remains in IE 6. 463 | */ 464 | 465 | input[type="checkbox"], 466 | input[type="radio"] { 467 | box-sizing: border-box; /* 1 */ 468 | padding: 0; /* 2 */ 469 | *height: 13px; /* 3 */ 470 | *width: 13px; /* 3 */ 471 | } 472 | 473 | /** 474 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 475 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 476 | * (include `-moz` to future-proof). 477 | */ 478 | 479 | input[type="search"] { 480 | -webkit-appearance: textfield; /* 1 */ 481 | -moz-box-sizing: content-box; 482 | -webkit-box-sizing: content-box; /* 2 */ 483 | box-sizing: content-box; 484 | } 485 | 486 | /** 487 | * Remove inner padding and search cancel button in Safari 5 and Chrome 488 | * on OS X. 489 | */ 490 | 491 | input[type="search"]::-webkit-search-cancel-button, 492 | input[type="search"]::-webkit-search-decoration { 493 | -webkit-appearance: none; 494 | } 495 | 496 | /** 497 | * Remove inner padding and border in Firefox 3+. 498 | */ 499 | 500 | button::-moz-focus-inner, 501 | input::-moz-focus-inner { 502 | border: 0; 503 | padding: 0; 504 | } 505 | 506 | /** 507 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 508 | * 2. Improve readability and alignment in all browsers. 509 | */ 510 | 511 | textarea { 512 | overflow: auto; /* 1 */ 513 | vertical-align: top; /* 2 */ 514 | } 515 | 516 | /* ========================================================================== 517 | Tables 518 | ========================================================================== */ 519 | 520 | /** 521 | * Remove most spacing between table cells. 522 | */ 523 | 524 | table { 525 | border-collapse: collapse; 526 | border-spacing: 0; 527 | } 528 | -------------------------------------------------------------------------------- /web/premises/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | import json 4 | from datetime import timedelta 5 | from markdown2 import markdown 6 | 7 | from django.contrib import messages 8 | from django.core.urlresolvers import reverse 9 | from django.utils import timezone 10 | from django.db.models import Max 11 | from django.utils.timezone import now 12 | from django.http import HttpResponse 13 | from django.shortcuts import get_object_or_404, redirect 14 | from django.template.loader import render_to_string 15 | from django.views.generic import DetailView, TemplateView, CreateView, View 16 | from django.views.generic.edit import UpdateView 17 | from django.db.models import Count 18 | 19 | from blog.models import Post 20 | from premises.utils import int_or_zero 21 | from premises.models import Contention, Premise 22 | from premises.forms import (ArgumentCreationForm, PremiseCreationForm, 23 | PremiseEditForm, ReportForm) 24 | from premises.signals import (added_premise_for_premise, 25 | added_premise_for_contention, reported_as_fallacy, 26 | supported_a_premise) 27 | 28 | 29 | class ContentionDetailView(DetailView): 30 | template_name = "premises/contention_detail.html" 31 | model = Contention 32 | 33 | def get_context_data(self, **kwargs): 34 | contention = self.get_object() 35 | view = ("list-view" if self.request.GET.get("view") == "list" 36 | else "tree-view") 37 | edit_mode = ( 38 | self.request.user.is_superuser or 39 | self.request.user.is_staff or 40 | contention.user == self.request.user) 41 | return super(ContentionDetailView, self).get_context_data( 42 | view=view, 43 | path=contention.get_absolute_url(), 44 | edit_mode=edit_mode, 45 | **kwargs) 46 | 47 | 48 | class ContentionJsonView(DetailView): 49 | model = Contention 50 | 51 | def render_to_response(self, context, **response_kwargs): 52 | contention = self.get_object(self.get_queryset()) 53 | return HttpResponse(json.dumps({ 54 | "nodes": self.build_tree(contention, self.request.user), 55 | }), content_type="application/json") 56 | 57 | def build_tree(self, contention, user): 58 | return { 59 | "name": contention.title, 60 | "parent": None, 61 | "pk": contention.pk, 62 | "owner": contention.owner, 63 | "sources": contention.sources, 64 | "is_singular": self.is_singular(contention), 65 | "children": self.get_premises(contention, user) 66 | } 67 | 68 | def get_premises(self, contention, user, parent=None): 69 | children = [{ 70 | "pk": premise.pk, 71 | "name": premise.text, 72 | "parent": parent.text if parent else None, 73 | "reportable_by_authenticated_user": self.user_can_report(premise, user), 74 | "report_count": premise.reports.count(), 75 | "user": { 76 | "id": premise.user.id, 77 | "username": premise.user.username, 78 | "absolute_url": reverse("auth_profile", 79 | args=[premise.user.username]) 80 | }, 81 | "sources": premise.sources, 82 | "premise_type": premise.premise_class(), 83 | "children": (self.get_premises(contention, user, parent=premise) 84 | if premise.published_children().exists() else []) 85 | } for premise in contention.published_premises(parent)] 86 | return children 87 | 88 | def user_can_report(self, premise, user): 89 | if user.is_authenticated() and user != premise.user: 90 | return not premise.reported_by(user) 91 | 92 | return False 93 | 94 | def is_singular(self, contention): 95 | result = (contention 96 | .premises 97 | .all() 98 | .aggregate(max_sibling=Max('sibling_count'))) 99 | return result['max_sibling'] <= 1 100 | 101 | 102 | class HomeView(TemplateView): 103 | template_name = "index.html" 104 | tab_class = "featured" 105 | 106 | paginate_by = 20 107 | 108 | def get_context_data(self, **kwargs): 109 | contentions = self.get_contentions() 110 | if self.request.user.is_authenticated(): 111 | notifications_qs = self.get_unread_notifications() 112 | notifications = list(notifications_qs) 113 | self.mark_as_read(notifications_qs) 114 | else: 115 | notifications = None 116 | return super(HomeView, self).get_context_data( 117 | next_page_url=self.get_next_page_url(), 118 | tab_class=self.tab_class, 119 | notifications=notifications, 120 | has_next_page=self.has_next_page(), 121 | announcements=self.get_announcements(), 122 | contentions=contentions, **kwargs) 123 | 124 | def get_announcements(self): 125 | return Post.objects.filter(is_announcement=True) 126 | 127 | def get_offset(self): 128 | return int_or_zero(self.request.GET.get("offset")) 129 | 130 | def get_limit(self): 131 | return self.get_offset() + self.paginate_by 132 | 133 | def has_next_page(self): 134 | total = self.get_contentions(paginate=False).count() 135 | return total > (self.get_offset() + self.paginate_by) 136 | 137 | def get_next_page_url(self): 138 | offset = self.get_offset() + self.paginate_by 139 | return '?offset=%(offset)s' % { 140 | "offset": offset 141 | } 142 | 143 | def get_unread_notifications(self): 144 | return (self.request.user 145 | .notifications 146 | .filter(is_read=False) 147 | [:5]) 148 | 149 | def mark_as_read(self, notifications): 150 | pks = notifications.values_list("id", flat=True) 151 | (self.request.user 152 | .notifications 153 | .filter(id__in=pks) 154 | .update(is_read=True)) 155 | 156 | def get_contentions(self, paginate=True): 157 | contentions = (Contention 158 | .objects 159 | .featured()) 160 | 161 | if paginate: 162 | contentions = (contentions[self.get_offset(): self.get_limit()]) 163 | 164 | return contentions 165 | 166 | 167 | class NotificationsView(HomeView): 168 | template_name = "notifications.html" 169 | 170 | def get_context_data(self, **kwargs): 171 | notifications_qs = self.request.user.notifications.all()[:40] 172 | notifications = list(notifications_qs) 173 | self.mark_as_read(notifications_qs) 174 | return super(HomeView, self).get_context_data( 175 | notifications=notifications, 176 | **kwargs) 177 | 178 | 179 | class SearchView(HomeView): 180 | tab_class = 'search' 181 | 182 | def get_context_data(self, **kwargs): 183 | return super(SearchView, self).get_context_data( 184 | keywords=self.get_keywords(), 185 | **kwargs 186 | ) 187 | 188 | def get_keywords(self): 189 | return self.request.GET.get('keywords') or "" 190 | 191 | def get_next_page_url(self): 192 | offset = self.get_offset() + self.paginate_by 193 | return '?offset=%(offset)s&keywords=%(keywords)s' % { 194 | "offset": offset, 195 | "keywords": self.get_keywords() 196 | } 197 | 198 | 199 | def get_contentions(self, paginate=True): 200 | keywords = self.request.GET.get('keywords') 201 | if not keywords or len(keywords) < 2: 202 | result = Contention.objects.none() 203 | else: 204 | result = (Contention 205 | .objects 206 | .filter(title__icontains=keywords)) 207 | 208 | if paginate: 209 | result = result[self.get_offset():self.get_limit()] 210 | 211 | return result 212 | 213 | 214 | class NewsView(HomeView): 215 | tab_class = "news" 216 | 217 | def get_contentions(self, paginate=True): 218 | contentions = Contention.objects.filter( 219 | is_published=True) 220 | 221 | if paginate: 222 | contentions = contentions[self.get_offset():self.get_limit()] 223 | 224 | return contentions 225 | 226 | 227 | class UpdatedArgumentsView(HomeView): 228 | tab_class = "updated" 229 | 230 | def get_contentions(self, paginate=True): 231 | contentions = (Contention 232 | .objects 233 | .filter(is_published=True) 234 | .order_by('-date_modification')) 235 | 236 | if paginate: 237 | contentions = contentions[self.get_offset():self.get_limit()] 238 | 239 | return contentions 240 | 241 | 242 | class ControversialArgumentsView(HomeView): 243 | tab_class = "controversial" 244 | 245 | def get_contentions(self, paginate=True): 246 | last_week = now() - timedelta(days=3) 247 | contentions = (Contention 248 | .objects 249 | .annotate(num_children=Count('premises')) 250 | .order_by('-num_children') 251 | .filter(date_modification__gte=last_week)) 252 | if paginate: 253 | return contentions[self.get_offset():self.get_limit()] 254 | 255 | return contentions 256 | 257 | 258 | class AboutView(TemplateView): 259 | template_name = "about.html" 260 | 261 | def get_context_data(self, **kwargs): 262 | content = markdown(render_to_string("about.md")) 263 | return super(AboutView, self).get_context_data( 264 | content=content, **kwargs) 265 | 266 | class TosView(TemplateView): 267 | template_name = "tos.html" 268 | 269 | def get_context_data(self, **kwargs): 270 | content = markdown(render_to_string("tos.md")) 271 | return super(TosView, self).get_context_data( 272 | content=content, **kwargs) 273 | 274 | 275 | class ArgumentCreationView(CreateView): 276 | template_name = "premises/new_contention.html" 277 | form_class = ArgumentCreationForm 278 | 279 | def form_valid(self, form): 280 | form.instance.user = self.request.user 281 | form.instance.ip_address = self.request.META['REMOTE_ADDR'] 282 | response = super(ArgumentCreationView, self).form_valid(form) 283 | form.instance.update_sibling_counts() 284 | return response 285 | 286 | 287 | class ArgumentUpdateView(UpdateView): 288 | template_name = "premises/edit_contention.html" 289 | form_class = ArgumentCreationForm 290 | 291 | def get_queryset(self): 292 | contentions = Contention.objects.all() 293 | if self.request.user.is_superuser: 294 | return contentions 295 | return contentions.filter(user=self.request.user) 296 | 297 | def form_valid(self, form): 298 | form.instance.user = self.request.user 299 | response = super(ArgumentUpdateView, self).form_valid(form) 300 | form.instance.update_sibling_counts() 301 | return response 302 | 303 | 304 | class ArgumentPublishView(DetailView): 305 | 306 | def get_queryset(self): 307 | return Contention.objects.filter(user=self.request.user) 308 | 309 | def post(self, request, slug): 310 | contention = self.get_object() 311 | contention.is_published = True 312 | contention.save() 313 | messages.info(request, u"Argüman yayına alındı.") 314 | return redirect(contention) 315 | 316 | 317 | class ArgumentUnpublishView(DetailView): 318 | 319 | def get_queryset(self): 320 | return Contention.objects.filter(user=self.request.user) 321 | 322 | def post(self, request, slug): 323 | contention = self.get_object() 324 | contention.is_published = False 325 | contention.save() 326 | messages.info(request, u"Argüman yayından kaldırıldı.") 327 | return redirect(contention) 328 | 329 | 330 | class ArgumentDeleteView(DetailView): 331 | 332 | def get_queryset(self): 333 | return Contention.objects.filter(user=self.request.user) 334 | 335 | def post(self, request, slug): 336 | contention = self.get_object() 337 | contention.delete() 338 | messages.info(request, u"Argümanınız silindi.") 339 | return redirect("home") 340 | 341 | delete = post 342 | 343 | 344 | class PremiseEditView(UpdateView): 345 | template_name = "premises/edit_premise.html" 346 | form_class = PremiseEditForm 347 | 348 | def get_queryset(self): 349 | premises = Premise.objects.all() 350 | if self.request.user.is_superuser: 351 | return premises 352 | return premises.filter(user=self.request.user) 353 | 354 | def form_valid(self, form): 355 | response = super(PremiseEditView, self).form_valid(form) 356 | form.instance.argument.update_sibling_counts() 357 | return response 358 | 359 | def get_context_data(self, **kwargs): 360 | return super(PremiseEditView, self).get_context_data( 361 | #contention=self.get_contention(), 362 | **kwargs) 363 | 364 | 365 | class PremiseCreationView(CreateView): 366 | template_name = "premises/new_premise.html" 367 | form_class = PremiseCreationForm 368 | 369 | def get_context_data(self, **kwargs): 370 | return super(PremiseCreationView, self).get_context_data( 371 | contention=self.get_contention(), 372 | parent=self.get_parent(), 373 | **kwargs) 374 | 375 | def form_valid(self, form): 376 | contention = self.get_contention() 377 | form.instance.user = self.request.user 378 | form.instance.argument = contention 379 | form.instance.parent = self.get_parent() 380 | form.instance.is_approved = True 381 | form.instance.ip_address = self.request.META['REMOTE_ADDR'] 382 | form.save() 383 | contention.update_sibling_counts() 384 | 385 | if form.instance.parent: 386 | added_premise_for_premise.send(sender=self, 387 | premise=form.instance) 388 | else: 389 | added_premise_for_contention.send(sender=self, 390 | premise=form.instance) 391 | 392 | contention.date_modification = timezone.now() 393 | contention.save() 394 | 395 | return redirect(contention) 396 | 397 | def get_contention(self): 398 | return get_object_or_404(Contention, slug=self.kwargs['slug']) 399 | 400 | def get_parent(self): 401 | parent_pk = self.kwargs.get("pk") 402 | if parent_pk: 403 | return get_object_or_404(Premise, pk=parent_pk) 404 | 405 | class PremiseSupportView(View): 406 | def get_premise(self): 407 | premises = Premise.objects.exclude(user=self.request.user) 408 | return get_object_or_404(premises, pk=self.kwargs['pk']) 409 | 410 | def post(self, request, *args, **kwargs): 411 | premise = self.get_premise() 412 | premise.supporters.add(self.request.user) 413 | supported_a_premise.send(sender=self, premise=premise, 414 | user=self.request.user) 415 | return redirect(self.get_contention()) 416 | 417 | def get_contention(self): 418 | return get_object_or_404(Contention, slug=self.kwargs['slug']) 419 | 420 | 421 | class PremiseUnsupportView(PremiseSupportView): 422 | def delete(self, request, *args, **kwargs): 423 | premise = self.get_premise() 424 | premise.supporters.remove(self.request.user) 425 | return redirect(self.get_contention()) 426 | 427 | post = delete 428 | 429 | 430 | class PremiseDeleteView(View): 431 | def get_premise(self): 432 | if self.request.user.is_staff: 433 | premises = Premise.objects.all() 434 | else: 435 | premises = Premise.objects.filter(user=self.request.user) 436 | return get_object_or_404(premises, 437 | pk=self.kwargs['pk']) 438 | 439 | def delete(self, request, *args, **kwargs): 440 | contention = self.get_premise() 441 | contention.delete() 442 | contention.update_sibling_counts() 443 | return redirect(self.get_contention()) 444 | 445 | post = delete 446 | 447 | def get_contention(self): 448 | return get_object_or_404(Contention, slug=self.kwargs['slug']) 449 | 450 | 451 | class ReportView(CreateView): 452 | form_class = ReportForm 453 | template_name = "premises/report.html" 454 | 455 | def get_context_data(self, **kwargs): 456 | return super(ReportView, self).get_context_data( 457 | premise=self.get_premise(), 458 | **kwargs) 459 | 460 | def get_contention(self): 461 | return get_object_or_404(Contention, slug=self.kwargs['slug']) 462 | 463 | def get_premise(self): 464 | return get_object_or_404(Premise, pk=self.kwargs['pk']) 465 | 466 | def form_valid(self, form): 467 | contention = self.get_contention() 468 | premise = self.get_premise() 469 | form.instance.contention = contention 470 | form.instance.premise = premise 471 | form.instance.reporter = self.request.user 472 | form.save() 473 | reported_as_fallacy.send(sender=self, report=form.instance) 474 | return redirect(contention) 475 | --------------------------------------------------------------------------------