├── .gitignore ├── LICENSE ├── README.md ├── media_cdn └── 2 │ └── django_plus_charts_js_share.png ├── requirements.txt ├── runtime.txt ├── src ├── cfelikes │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── db.sqlite3 ├── manage.py ├── posts │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20170309_2235.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── posts │ │ │ ├── post_detail.html │ │ │ ├── post_form.html │ │ │ └── post_list.html │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── static │ └── css │ │ └── main.css └── templates │ ├── base.html │ └── base │ ├── bootstrap_defaults.html │ ├── css.html │ └── js.html └── static_cdn ├── admin ├── css │ ├── base.css │ ├── changelists.css │ ├── dashboard.css │ ├── fonts.css │ ├── forms.css │ ├── login.css │ ├── rtl.css │ └── widgets.css ├── fonts │ ├── LICENSE.txt │ ├── README.txt │ ├── Roboto-Bold-webfont.woff │ ├── Roboto-Light-webfont.woff │ └── Roboto-Regular-webfont.woff ├── img │ ├── LICENSE │ ├── README.txt │ ├── calendar-icons.svg │ ├── gis │ │ ├── move_vertex_off.svg │ │ └── move_vertex_on.svg │ ├── icon-addlink.svg │ ├── icon-alert.svg │ ├── icon-calendar.svg │ ├── icon-changelink.svg │ ├── icon-clock.svg │ ├── icon-deletelink.svg │ ├── icon-no.svg │ ├── icon-unknown-alt.svg │ ├── icon-unknown.svg │ ├── icon-yes.svg │ ├── inline-delete.svg │ ├── search.svg │ ├── selector-icons.svg │ ├── sorting-icons.svg │ ├── tooltag-add.svg │ └── tooltag-arrowright.svg └── js │ ├── SelectBox.js │ ├── SelectFilter2.js │ ├── actions.js │ ├── actions.min.js │ ├── admin │ ├── DateTimeShortcuts.js │ └── RelatedObjectLookups.js │ ├── calendar.js │ ├── cancel.js │ ├── change_form.js │ ├── collapse.js │ ├── collapse.min.js │ ├── core.js │ ├── inlines.js │ ├── inlines.min.js │ ├── jquery.init.js │ ├── popup_response.js │ ├── prepopulate.js │ ├── prepopulate.min.js │ ├── prepopulate_init.js │ ├── timeparse.js │ ├── urlify.js │ └── vendor │ ├── jquery │ ├── LICENSE-JQUERY.txt │ ├── jquery.js │ └── jquery.min.js │ └── xregexp │ ├── LICENSE-XREGEXP.txt │ ├── xregexp.js │ └── xregexp.min.js ├── css └── main.css └── rest_framework ├── css ├── bootstrap-tweaks.css ├── bootstrap.min.css ├── default.css └── prettify.css ├── docs ├── css │ ├── base.css │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ ├── font-awesome-4.0.3.css │ ├── highlight.css │ └── jquery.json-view.min.css ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── img │ ├── favicon.ico │ └── grid.png └── js │ ├── api.js │ ├── base.js │ ├── bootstrap.min.js │ ├── highlight.pack.js │ ├── jquery-1.10.2.min.js │ └── jquery.json-view.min.js ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf ├── glyphicons-halflings-regular.woff └── glyphicons-halflings-regular.woff2 ├── img ├── glyphicons-halflings-white.png ├── glyphicons-halflings.png └── grid.png └── js ├── ajax-form.js ├── bootstrap.min.js ├── coreapi-0.1.0.js ├── csrf.js ├── default.js ├── jquery-1.12.4.min.js └── prettify-min.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin/ 3 | lib/ 4 | include/ 5 | pip-selfcheck.json 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *,cover 52 | .hypothesis/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # IPython Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Coding For Entrepreneurs 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Django Likes 4 | Learn how to create a ajax-powered `like` button in Django using `jQuery`. 5 | 6 | We show you how to create a `like` button much like what you'd see on Facebook, Instagram, Google+, YouTube, and others. We are using the [Django](http://django.project.com) web framework along with the [Django Rest Framework](http://www.django-rest-framework.org/) and [jQuery](http://jquery.com/). The key here in this series is how to setup Django's backend so your project can collect `like` data. We use `jQuery` as a easy-to-implement solution for working with Django but really, this method could be used with any client technology like Angular, React, Swift, Java, and more. 7 | 8 | Watch it [here](https://www.codingforentrepreneurs.com/projects/django-likes/) 9 | 10 | What to see more Ajax? Submit & Upvote [here](http://joincfe.com/suggest/) 11 | 12 | Subscribe to our YouTube channel: [http://joincfe.com/youtube/](http://joincfe.com/youtube/) 13 | 14 | 15 | ### Code History 16 | [Base project setup](../../tree/8be3ff8ffc5fd3922a8e27da156c093065830707) 17 | 18 | [Django Likes](../../tree/6179763b6689e1fcb80ce87e1008a84517a52c41) -------------------------------------------------------------------------------- /media_cdn/2/django_plus_charts_js_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/media_cdn/2/django_plus_charts_js_share.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.2 2 | Django==1.10.6 3 | djangorestframework==3.6.1 4 | django-crispy-forms==1.6.1 5 | olefile==0.44 6 | packaging==16.8 7 | Pillow==4.0.0 8 | pyparsing==2.1.10 9 | six==1.10.0 10 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.0 2 | -------------------------------------------------------------------------------- /src/cfelikes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/src/cfelikes/__init__.py -------------------------------------------------------------------------------- /src/cfelikes/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for cfelikes project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.10.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.10/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '-3dixy!-cnkpoale2p2oqarzjth$=_5v50uc6&ka8i#$_0^x+y' 24 | 25 | 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 | 'crispy_forms', 40 | 'rest_framework', 41 | 42 | 'posts', 43 | ] 44 | 45 | CRISPY_TEMPLATE_PACK = 'bootstrap3' 46 | 47 | MIDDLEWARE = [ 48 | 'django.middleware.security.SecurityMiddleware', 49 | 'django.contrib.sessions.middleware.SessionMiddleware', 50 | 'django.middleware.common.CommonMiddleware', 51 | 'django.middleware.csrf.CsrfViewMiddleware', 52 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | ] 56 | 57 | ROOT_URLCONF = 'cfelikes.urls' 58 | 59 | TEMPLATES = [ 60 | { 61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 62 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 63 | 'APP_DIRS': True, 64 | 'OPTIONS': { 65 | 'context_processors': [ 66 | 'django.template.context_processors.debug', 67 | 'django.template.context_processors.request', 68 | 'django.contrib.auth.context_processors.auth', 69 | 'django.contrib.messages.context_processors.messages', 70 | ], 71 | }, 72 | }, 73 | ] 74 | 75 | WSGI_APPLICATION = 'cfelikes.wsgi.application' 76 | 77 | 78 | # Database 79 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases 80 | 81 | DATABASES = { 82 | 'default': { 83 | 'ENGINE': 'django.db.backends.sqlite3', 84 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 85 | } 86 | } 87 | 88 | 89 | # Password validation 90 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators 91 | 92 | AUTH_PASSWORD_VALIDATORS = [ 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 104 | }, 105 | ] 106 | 107 | 108 | # Internationalization 109 | # https://docs.djangoproject.com/en/1.10/topics/i18n/ 110 | 111 | LANGUAGE_CODE = 'en-us' 112 | 113 | TIME_ZONE = 'UTC' 114 | 115 | USE_I18N = True 116 | 117 | USE_L10N = True 118 | 119 | USE_TZ = True 120 | 121 | 122 | # Static files (CSS, JavaScript, Images) 123 | # https://docs.djangoproject.com/en/1.10/howto/static-files/ 124 | 125 | STATIC_URL = '/static/' 126 | 127 | STATICFILES_DIRS = [ 128 | os.path.join(BASE_DIR, "static"), 129 | #'/var/www/static/', 130 | ] 131 | 132 | STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static_cdn") 133 | 134 | MEDIA_URL = "/media/" 135 | MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media_cdn") 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/cfelikes/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls import include, url 3 | from django.conf.urls.static import static 4 | from django.contrib import admin 5 | 6 | urlpatterns = [ 7 | url(r'^admin/', admin.site.urls), 8 | url(r'^', include("posts.urls", namespace='posts')), 9 | ] 10 | 11 | 12 | if settings.DEBUG: 13 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 14 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -------------------------------------------------------------------------------- /src/cfelikes/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for likes 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.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cfelikes.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /src/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/src/db.sqlite3 -------------------------------------------------------------------------------- /src/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", "cfelikes.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /src/posts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/src/posts/__init__.py -------------------------------------------------------------------------------- /src/posts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Post 4 | 5 | class PostModelAdmin(admin.ModelAdmin): 6 | list_display = ["title", "updated", "timestamp"] 7 | list_display_links = ["updated"] 8 | list_editable = ["title"] 9 | list_filter = ["updated", "timestamp"] 10 | 11 | search_fields = ["title", "content"] 12 | class Meta: 13 | model = Post 14 | 15 | 16 | admin.site.register(Post, PostModelAdmin) -------------------------------------------------------------------------------- /src/posts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PostsConfig(AppConfig): 5 | name = 'posts' 6 | -------------------------------------------------------------------------------- /src/posts/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from .models import Post 4 | 5 | 6 | class PostForm(forms.ModelForm): 7 | publish = forms.DateField(widget=forms.SelectDateWidget) 8 | class Meta: 9 | model = Post 10 | fields = [ 11 | "title", 12 | "content", 13 | "image", 14 | "draft", 15 | "publish", 16 | ] -------------------------------------------------------------------------------- /src/posts/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.6 on 2017-03-09 21:35 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import posts.models 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='Post', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('title', models.CharField(max_length=120)), 25 | ('slug', models.SlugField(unique=True)), 26 | ('image', models.ImageField(blank=True, height_field='height_field', null=True, upload_to=posts.models.upload_location, width_field='width_field')), 27 | ('height_field', models.IntegerField(default=0)), 28 | ('width_field', models.IntegerField(default=0)), 29 | ('content', models.TextField()), 30 | ('draft', models.BooleanField(default=False)), 31 | ('publish', models.DateField()), 32 | ('read_time', models.IntegerField(default=0)), 33 | ('updated', models.DateTimeField(auto_now=True)), 34 | ('timestamp', models.DateTimeField(auto_now_add=True)), 35 | ('user', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 36 | ], 37 | options={ 38 | 'ordering': ['-timestamp', '-updated'], 39 | }, 40 | ), 41 | ] 42 | -------------------------------------------------------------------------------- /src/posts/migrations/0002_auto_20170309_2235.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.6 on 2017-03-09 22:35 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('posts', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='post', 19 | name='likes', 20 | field=models.ManyToManyField(blank=True, related_name='post_likes', to=settings.AUTH_USER_MODEL), 21 | ), 22 | migrations.AlterField( 23 | model_name='post', 24 | name='slug', 25 | field=models.SlugField(blank=True, unique=True), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /src/posts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/src/posts/migrations/__init__.py -------------------------------------------------------------------------------- /src/posts/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf import settings 4 | from django.contrib.contenttypes.models import ContentType 5 | from django.core.urlresolvers import reverse 6 | from django.db import models 7 | from django.db.models.signals import pre_save 8 | from django.utils import timezone 9 | from django.utils.safestring import mark_safe 10 | from django.utils.text import slugify 11 | 12 | from .utils import get_read_time, unique_slug_generator 13 | 14 | class PostManager(models.Manager): 15 | def active(self, *args, **kwargs): 16 | qs = self.get_queryset().filter( 17 | draft=False, 18 | publish__lte=timezone.now() 19 | ) 20 | return qs 21 | 22 | 23 | def upload_location(instance, filename): 24 | PostModel = instance.__class__ 25 | new_id = PostModel.objects.order_by("id").last().id + 1 26 | return "%s/%s" %(new_id, filename) 27 | 28 | class Post(models.Model): 29 | user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1) 30 | title = models.CharField(max_length=120) 31 | slug = models.SlugField(unique=True, blank=True) 32 | image = models.ImageField(upload_to=upload_location, 33 | null=True, 34 | blank=True, 35 | width_field="width_field", 36 | height_field="height_field") 37 | likes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='post_likes') 38 | height_field = models.IntegerField(default=0) 39 | width_field = models.IntegerField(default=0) 40 | content = models.TextField() 41 | draft = models.BooleanField(default=False) 42 | publish = models.DateField(auto_now=False, auto_now_add=False) 43 | read_time = models.IntegerField(default=0) 44 | updated = models.DateTimeField(auto_now=True, auto_now_add=False) 45 | timestamp = models.DateTimeField(auto_now=False, auto_now_add=True) 46 | 47 | objects = PostManager() 48 | 49 | def __unicode__(self): 50 | return self.title 51 | 52 | def __str__(self): 53 | return self.title 54 | 55 | def get_absolute_url(self): 56 | return reverse("posts:detail", kwargs={"slug": self.slug}) 57 | 58 | # def get_api_url(self): 59 | # return reverse("posts-api:detail", kwargs={"slug": self.slug}) 60 | 61 | def get_like_url(self): 62 | return reverse("posts:like-toggle", kwargs={"slug": self.slug}) 63 | 64 | def get_api_like_url(self): 65 | return reverse("posts:like-api-toggle", kwargs={"slug": self.slug}) 66 | 67 | class Meta: 68 | ordering = ["-timestamp", "-updated"] 69 | 70 | @property 71 | def get_content_type(self): 72 | instance = self 73 | content_type = ContentType.objects.get_for_model(instance.__class__) 74 | return content_type 75 | 76 | 77 | 78 | def pre_save_post_receiver(sender, instance, *args, **kwargs): 79 | if not instance.slug: 80 | instance.slug = unique_slug_generator(instance) 81 | 82 | if instance.content: 83 | read_time_var = get_read_time(instance.content) 84 | instance.read_time = read_time_var 85 | 86 | 87 | pre_save.connect(pre_save_post_receiver, sender=Post) 88 | -------------------------------------------------------------------------------- /src/posts/templates/posts/post_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load crispy_forms_tags %} 3 | 4 | 5 | {% block head_title %} 6 | {{ instance.title }} | {{ block.super }} 7 | {% endblock head_title %} 8 | 9 | 10 | {% block post_detail_link %} 11 |
Read time: {% if instance.read_time <= 1 %} < 1 Minute {% else %}{{ instance.read_time }} minutes {% endif %}
27 |{{ instance.likes.count }} Like
28 | {% if instance.user.get_full_name %} 29 |Author: {{ instance.user.get_full_name }}
30 | {% endif %} 31 | 32 | 33 |Author: {{ obj.user.get_full_name }}
{% endif %} 30 | {{ obj.get_markdown|truncatechars_html:120 }} 31 | 32 | to respect formatting
19 | }
20 |
21 | .main-container {
22 | padding-left: 30px;
23 | padding-right: 30px;
24 | }
25 |
26 | .btn:focus,
27 | .btn:focus:active {
28 | outline: none;
29 | }
30 |
31 | .sidebar {
32 | overflow: auto;
33 | font-family: verdana;
34 | font-size: 12px;
35 | font-weight: 200;
36 | background-color: #2e353d;
37 | position: fixed;
38 | top: 0px;
39 | width: 225px;
40 | height: 100%;
41 | color: #FFF;
42 | }
43 |
44 | .sidebar .brand {
45 | background-color: #23282e;
46 | display: block;
47 | text-align: center;
48 | padding: 25px 0;
49 | margin-top: 0;
50 | margin-bottom: 0;
51 | }
52 |
53 | .sidebar .brand a {
54 | color: #FFF;
55 | }
56 |
57 | .sidebar .brand a:hover,
58 | .sidebar .brand a:active,
59 | .sidebar .brand a:focus {
60 | text-decoration: none;
61 | }
62 |
63 | .sidebar .toggle-btn {
64 | display: none;
65 | }
66 |
67 | .sidebar .menu-list ul,
68 | .sidebar .menu-list li {
69 | background: #2e353d;
70 | list-style: none;
71 | padding: 0px;
72 | margin: 0px;
73 | line-height: 35px;
74 | cursor: pointer;
75 | }
76 |
77 | .sidebar .menu-list ul :not(collapsed) .arrow:before,
78 | .sidebar .menu-list li :not(collapsed) .arrow:before {
79 | font-family: FontAwesome;
80 | content: "\f078";
81 | display: inline-block;
82 | padding-left: 10px;
83 | padding-right: 10px;
84 | vertical-align: middle;
85 | float: right;
86 | }
87 |
88 | .sidebar .menu-list ul .active,
89 | .sidebar .menu-list li .active {
90 | border-left: 3px solid #d19b3d;
91 | background-color: #4f5b69;
92 | }
93 |
94 | .sidebar .menu-list ul .sub-menu li.active,
95 | .sidebar .menu-list li .sub-menu li.active {
96 | color: #d19b3d;
97 | }
98 |
99 | .sidebar .menu-list ul .sub-menu li.active a,
100 | .sidebar .menu-list li .sub-menu li.active a {
101 | color: #d19b3d;
102 | }
103 |
104 | .sidebar .menu-list ul .sub-menu li,
105 | .sidebar .menu-list li .sub-menu li {
106 | background-color: #181c20;
107 | border: none;
108 | border-bottom: 1px solid #23282e;
109 | margin-left: 0px;
110 | text-indent: 10px;
111 | }
112 |
113 | .sidebar .menu-list ul .sub-menu li:hover,
114 | .sidebar .menu-list li .sub-menu li:hover {
115 | background-color: #020203;
116 | }
117 |
118 |
119 | .sidebar .menu-list ul .sub-menu li a,
120 | .sidebar .menu-list li .sub-menu li a {
121 | display: block;
122 | }
123 |
124 | .sidebar .menu-list ul .sub-menu li a:before,
125 | .sidebar .menu-list li .sub-menu li a:before {
126 | font-family: FontAwesome;
127 | content: "\f105";
128 | display: inline-block;
129 | padding-left: 10px;
130 | padding-right: 10px;
131 | vertical-align: middle;
132 | }
133 |
134 | .sidebar .menu-list li {
135 | padding-left: 0px;
136 | border-left: 3px solid #2e353d;
137 | border-bottom: 1px solid #23282e;
138 | }
139 |
140 | .sidebar .menu-list li a {
141 | text-decoration: none;
142 | color: white;
143 | }
144 |
145 | .sidebar .menu-list li a i {
146 | padding-left: 10px;
147 | width: 20px;
148 | padding-right: 20px;
149 | }
150 |
151 | .sidebar .menu-list li:hover {
152 | border-left: 3px solid #d19b3d;
153 | background-color: #4f5b69;
154 | -webkit-transition: all 1s ease;
155 | -moz-transition: all 1s ease;
156 | -o-transition: all 1s ease;
157 | -ms-transition: all 1s ease;
158 | transition: all 1s ease;
159 | }
160 |
161 | body {
162 | margin: 0px;
163 | padding: 0px;
164 | }
165 |
166 | .coredocs-section-title {
167 | margin-top: 20px;
168 | padding-bottom: 10px;
169 | border-bottom: 1px solid lightgrey;
170 | }
171 |
172 | .coredocs-link-title a,
173 | .coredocs-section-title a {
174 | display: none;
175 | }
176 |
177 | .coredocs-link-title a,
178 | .coredocs-section-title a {
179 | text-decoration: none;
180 | }
181 |
182 | .coredocs-link-title:hover a,
183 | .coredocs-section-title:hover a {
184 | display: inline;
185 | font-size: 20px;
186 | }
187 |
188 | .coredocs-section-title:last-child {
189 | margin-top: 0;
190 | }
191 |
192 |
193 | /* @group Language Switcher */
194 |
195 | .sidebar .menu-list.menu-list-bottom {
196 | margin-bottom: 0;
197 | position: absolute;
198 | bottom: 0;
199 | left: 0;
200 | right: 0;
201 | border-top: 1px solid #23282e;
202 | }
203 |
204 | .sidebar .menu-list-bottom li span {
205 | float: right;
206 | margin-right: 20px;
207 | color: #d19b3d;
208 | }
209 |
210 | /* @end Language Switcher */
211 |
212 |
213 | /* @group Docs Content */
214 |
215 | .docs-content .meta .label {
216 | vertical-align: middle;
217 | font-size: 14px;
218 | font-weight: normal;
219 | }
220 |
221 | .docs-content .meta code {
222 | vertical-align: middle;
223 | padding: .2em .6em .3em;
224 | font-size: 14px;
225 | }
226 |
227 | .docs-content .btn {
228 | font-size: inherit;
229 | }
230 |
231 | .code-samples pre {
232 | margin-top: 20px;
233 | }
234 |
235 | /* @end Docs Content */
236 |
237 |
238 | @media (max-width: 767px) {
239 | .main-container {
240 | padding-left: 15px;
241 | padding-right: 15px;
242 | }
243 |
244 | .sidebar {
245 | position: relative;
246 | width: 100%;
247 | margin-bottom: 10px;
248 | overflow: visible;
249 | }
250 |
251 | .sidebar .toggle-btn {
252 | display: block;
253 | cursor: pointer;
254 | position: absolute;
255 | right: 10px;
256 | top: 10px;
257 | z-index: 10 !important;
258 | padding: 3px;
259 | width: 40px;
260 | text-align: center;
261 | }
262 |
263 | .sidebar .menu-list.menu-list-bottom {
264 | position: static;
265 | }
266 |
267 | .sidebar .brand {
268 | margin-top: 0;
269 | margin-bottom: 0;
270 |
271 | text-align: left !important;
272 | font-size: 22px;
273 | padding: 0;
274 | padding-left: 20px;
275 | line-height: 50px !important;
276 | }
277 | }
278 |
279 | @media (min-width: 767px) {
280 | .sidebar .menu-list .menu-content {
281 | display: block;
282 | }
283 | #main {
284 | width:calc(100% - 225px);
285 | float: right;
286 | }
287 | }
288 |
289 | @media (min-width: 992px) {
290 | .modal-lg {
291 | width: 980px;
292 | }
293 | }
294 |
295 | .api-modal .modal-title .fa {
296 | color: #93c54b;
297 | }
298 |
299 | .api-modal .modal-body .request-awaiting {
300 | padding: 35px 10px;
301 | color: #7F8177;
302 | text-align: center;
303 | }
304 |
305 | .api-modal .modal-body .meta {
306 | margin-bottom: 20px;
307 | }
308 |
309 | .api-modal .modal-body .meta .label {
310 | vertical-align: middle;
311 | font-size: 14px;
312 | font-weight: normal;
313 | }
314 |
315 | .api-modal .modal-body .meta code {
316 | vertical-align: middle;
317 | padding: .2em .6em .3em;
318 | font-size: 14px;
319 | }
320 |
321 | .api-modal .modal-content .toggle-view {
322 | text-align: right;
323 | float: right;
324 | }
325 |
326 | .api-modal .modal-content .response .well {
327 | margin: 0;
328 | max-height: 550px;
329 | }
330 |
331 | .highlight {
332 | background-color: #f7f7f9
333 | }
334 |
335 | .checkbox label.control-label {
336 | font-weight: bold
337 | }
338 |
339 | @media (min-width: 768px) {
340 | .navbar-nav.navbar-right:last-child {
341 | margin-right: 0 !important;
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/css/highlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | This is the GitHub theme for highlight.js
3 |
4 | github.com style (c) Vasily Polovnyov
5 |
6 | */
7 |
8 | .hljs {
9 | display: block;
10 | overflow-x: auto;
11 | padding: 0.5em;
12 | color: #333;
13 | -webkit-text-size-adjust: none;
14 | }
15 |
16 | .hljs-comment,
17 | .diff .hljs-header,
18 | .hljs-javadoc {
19 | color: #998;
20 | font-style: italic;
21 | }
22 |
23 | .hljs-keyword,
24 | .css .rule .hljs-keyword,
25 | .hljs-winutils,
26 | .nginx .hljs-title,
27 | .hljs-subst,
28 | .hljs-request,
29 | .hljs-status {
30 | color: #333;
31 | font-weight: bold;
32 | }
33 |
34 | .hljs-number,
35 | .hljs-hexcolor,
36 | .ruby .hljs-constant {
37 | color: #008080;
38 | }
39 |
40 | .hljs-string,
41 | .hljs-tag .hljs-value,
42 | .hljs-phpdoc,
43 | .hljs-dartdoc,
44 | .tex .hljs-formula {
45 | color: #d14;
46 | }
47 |
48 | .hljs-title,
49 | .hljs-id,
50 | .scss .hljs-preprocessor {
51 | color: #900;
52 | font-weight: bold;
53 | }
54 |
55 | .hljs-list .hljs-keyword,
56 | .hljs-subst {
57 | font-weight: normal;
58 | }
59 |
60 | .hljs-class .hljs-title,
61 | .hljs-type,
62 | .vhdl .hljs-literal,
63 | .tex .hljs-command {
64 | color: #458;
65 | font-weight: bold;
66 | }
67 |
68 | .hljs-tag,
69 | .hljs-tag .hljs-title,
70 | .hljs-rule .hljs-property,
71 | .django .hljs-tag .hljs-keyword {
72 | color: #000080;
73 | font-weight: normal;
74 | }
75 |
76 | .hljs-attribute,
77 | .hljs-variable,
78 | .lisp .hljs-body,
79 | .hljs-name {
80 | color: #008080;
81 | }
82 |
83 | .hljs-regexp {
84 | color: #009926;
85 | }
86 |
87 | .hljs-symbol,
88 | .ruby .hljs-symbol .hljs-string,
89 | .lisp .hljs-keyword,
90 | .clojure .hljs-keyword,
91 | .scheme .hljs-keyword,
92 | .tex .hljs-special,
93 | .hljs-prompt {
94 | color: #990073;
95 | }
96 |
97 | .hljs-built_in {
98 | color: #0086b3;
99 | }
100 |
101 | .hljs-preprocessor,
102 | .hljs-pragma,
103 | .hljs-pi,
104 | .hljs-doctype,
105 | .hljs-shebang,
106 | .hljs-cdata {
107 | color: #999;
108 | font-weight: bold;
109 | }
110 |
111 | .hljs-deletion {
112 | background: #fdd;
113 | }
114 |
115 | .hljs-addition {
116 | background: #dfd;
117 | }
118 |
119 | .diff .hljs-change {
120 | background: #0086b3;
121 | }
122 |
123 | .hljs-chunk {
124 | color: #aaa;
125 | }
126 |
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/css/jquery.json-view.min.css:
--------------------------------------------------------------------------------
1 | .json-view{position:relative}
2 | .json-view .collapser{width:20px;height:18px;display:block;position:absolute;left:-1.7em;top:-.2em;z-index:5;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYGBgOADE%2F3Hgw0DM4IRHgSsDFOzFInmMAQnY49ONzZRjDFiADT7dMLALiE8y4AGW6LoBAgwAuIkf%2F%2FB7O9sAAAAASUVORK5CYII%3D);background-repeat:no-repeat;background-position:center center;opacity:.5;cursor:pointer}
3 | .json-view .collapsed{-ms-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-khtml-transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}
4 | .json-view .bl{display:block;padding-left:20px;margin-left:-20px;position:relative}
5 | .json-view{font-family:monospace}
6 | .json-view ul{list-style-type:none;padding-left:2em;border-left:1px dotted;margin:.3em}
7 | .json-view ul li{position:relative}
8 | .json-view .comments,.json-view .dots{display:none;-moz-user-select:none;-ms-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}
9 | .json-view .comments{padding-left:.8em;font-style:italic;color:#888}
10 | .json-view .bool,.json-view .null,.json-view .num,.json-view .undef{font-weight:700;color:#1A01CC}
11 | .json-view .str{color:#800}
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/docs/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/docs/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/docs/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/docs/img/favicon.ico
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/docs/img/grid.png
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/js/api.js:
--------------------------------------------------------------------------------
1 | function normalizeHTTPHeader (str) {
2 | // Capitalize HTTP headers for display.
3 | return (str.charAt(0).toUpperCase() + str.substring(1))
4 | .replace(/-(.)/g, function ($1) { return $1.toUpperCase() })
5 | .replace(/(Www)/g, function ($1) { return 'WWW' })
6 | .replace(/(Xss)/g, function ($1) { return 'XSS' })
7 | .replace(/(Md5)/g, function ($1) { return 'MD5' })
8 | }
9 |
10 | let responseDisplay = 'data'
11 | const coreapi = window.coreapi
12 | const schema = window.schema
13 |
14 | // Language Control
15 | $('#language-control li').click(function (event) {
16 | event.preventDefault();
17 | const languageMenuItem = $(this).find('a');
18 | var language = languageMenuItem.data("language")
19 |
20 | var languageControls = $(this).closest('ul').find('li');
21 | languageControls.find('a').not('[data-language="' + language +'"]').parent().removeClass("active")
22 | languageControls.find('a').filter('[data-language="' + language +'"]').parent().addClass("active")
23 |
24 | $('#selected-language').text(language)
25 |
26 | var codeBlocks = $('pre.highlight')
27 | codeBlocks.not('[data-language="' + language +'"]').addClass("hide")
28 | codeBlocks.filter('[data-language="' + language +'"]').removeClass("hide")
29 | })
30 |
31 | // API Explorer
32 | $('form.api-interaction').submit(function(event) {
33 | event.preventDefault();
34 |
35 | const form = $(this).closest("form");
36 | const key = form.data("key");
37 | var params = {};
38 |
39 | const formData = new FormData(form.get()[0]);
40 | for (var [paramKey, paramValue] of formData.entries()) {
41 | var elem = form.find("[name=" + paramKey + "]")
42 | var dataType = elem.data('type') || 'string'
43 |
44 | if (dataType === 'integer' && paramValue) {
45 | let value = parseInt(paramValue)
46 | if (!isNaN(value)) {
47 | params[paramKey] = value
48 | }
49 | } else if (dataType === 'number' && paramValue) {
50 | let value = parseFloat(paramValue)
51 | if (!isNaN(value)) {
52 | params[paramKey] = value
53 | }
54 | } else if (dataType === 'boolean' && paramValue) {
55 | let value = {
56 | 'true': true,
57 | 'false': false
58 | }[paramValue.toLowerCase()]
59 | if (value !== undefined) {
60 | params[paramKey]
61 | }
62 | } else if (dataType === 'array' && paramValue) {
63 | try {
64 | params[paramKey] = JSON.parse(paramValue)
65 | } catch (err) {
66 | // Ignore malformed JSON
67 | }
68 | } else if (dataType === 'object' && paramValue) {
69 | try {
70 | params[paramKey] = JSON.parse(paramValue)
71 | } catch (err) {
72 | // Ignore malformed JSON
73 | }
74 | } else if (dataType === 'string' && paramValue) {
75 | params[paramKey] = paramValue
76 | }
77 | }
78 |
79 | form.find(":checkbox").each(function( index ) {
80 | // Handle unselected checkboxes
81 | var name = $(this).attr("name");
82 | if (!params.hasOwnProperty(name)) {
83 | params[name] = false
84 | }
85 | })
86 |
87 | function requestCallback(request) {
88 | // Fill in the "GET /foo/" display.
89 | let parser = document.createElement('a');
90 | parser.href = request.url;
91 | const method = request.options.method
92 | const path = parser.pathname + parser.hash + parser.search
93 |
94 | form.find(".request-method").text(method)
95 | form.find(".request-url").text(path)
96 | }
97 |
98 | function responseCallback(response, responseText) {
99 | // Display the 'Data'/'Raw' control.
100 | form.closest(".modal-content").find(".toggle-view").removeClass("hide")
101 |
102 | // Fill in the "200 OK" display.
103 | form.find(".response-status-code").removeClass("label-success").removeClass("label-danger")
104 | if (response.ok) {
105 | form.find(".response-status-code").addClass("label-success")
106 | } else {
107 | form.find(".response-status-code").addClass("label-danger")
108 | }
109 | form.find(".response-status-code").text(response.status)
110 | form.find(".meta").removeClass("hide")
111 |
112 | // Fill in the Raw HTTP response display.
113 | var panelText = 'HTTP/1.1 ' + response.status + ' ' + response.statusText + '\n';
114 | response.headers.forEach((header, key) => {
115 | panelText += normalizeHTTPHeader(key) + ': ' + header + '\n'
116 | })
117 | if (responseText) {
118 | panelText += '\n' + responseText
119 | }
120 | form.find(".response-raw-response").text(panelText)
121 | }
122 |
123 | // Instantiate a client to make the outgoing request.
124 | let options = {
125 | requestCallback: requestCallback,
126 | responseCallback: responseCallback,
127 | }
128 |
129 | // Setup authentication options.
130 | if (window.auth && window.auth.type === 'token') {
131 | // Header authentication
132 | options.auth = new coreapi.auth.TokenAuthentication({
133 | prefix: window.auth.scheme,
134 | token: window.auth.token
135 | })
136 | } else if (window.auth && window.auth.type === 'basic') {
137 | // Basic authentication
138 | options.auth = new coreapi.auth.BasicAuthentication({
139 | username: window.auth.username,
140 | password: window.auth.password
141 | })
142 | } else if (window.auth && window.auth.type === 'session') {
143 | // Session authentication
144 | options.auth = new coreapi.auth.SessionAuthentication({
145 | csrfCookieName: 'csrftoken',
146 | csrfHeaderName: 'X-CSRFToken'
147 | })
148 | }
149 |
150 | const client = new coreapi.Client(options)
151 |
152 | client.action(schema, key, params).then(function (data) {
153 | var response = JSON.stringify(data, null, 2);
154 | form.find(".request-awaiting").addClass("hide")
155 | form.find(".response-raw").addClass("hide")
156 | form.find(".response-data").addClass("hide")
157 | form.find(".response-data").text('')
158 | form.find(".response-data").jsonView(response)
159 |
160 | if (responseDisplay === 'data') {
161 | form.find(".response-data").removeClass("hide")
162 | } else {
163 | form.find(".response-raw").removeClass("hide")
164 | }
165 | }).catch(function (error) {
166 | var response = JSON.stringify(error.content, null, 2);
167 | form.find(".request-awaiting").addClass("hide")
168 | form.find(".response-raw").addClass("hide")
169 | form.find(".response-data").addClass("hide")
170 | form.find(".response-data").text('')
171 | form.find(".response-data").jsonView(response)
172 |
173 | if (responseDisplay === 'data') {
174 | form.find(".response-data").removeClass("hide")
175 | } else {
176 | form.find(".response-raw").removeClass("hide")
177 | }
178 | })
179 | });
180 |
181 | // 'Data'/'Raw' control
182 | $('.toggle-view button').click(function() {
183 | responseDisplay = $(this).data("display-toggle");
184 | $(this).removeClass("btn-default").addClass('btn-info').siblings().removeClass('btn-info');
185 | if (responseDisplay === 'raw') {
186 | $(this).closest(".modal-content").find(".response-raw").removeClass("hide");
187 | $(this).closest(".modal-content").find(".response-data").addClass("hide");
188 | } else {
189 | $(this).closest(".modal-content").find(".response-data").removeClass("hide");
190 | $(this).closest(".modal-content").find(".response-raw").addClass("hide");
191 | }
192 | });
193 |
194 | // Authentication: none
195 | $('#auth-control').find("[data-auth='none']").click(function (event) {
196 | event.preventDefault();
197 | window.auth = null;
198 | $('#selected-authentication').text('none');
199 | $('#auth-control').children().removeClass('active');
200 | $('#auth-control').find("[data-auth='none']").addClass('active');
201 | })
202 |
203 | // Authentication: token
204 | $('form.authentication-token-form').submit(function(event) {
205 | event.preventDefault();
206 | const form = $(this).closest("form");
207 | const scheme = form.find('input#scheme').val();
208 | const token = form.find('input#token').val();
209 | window.auth = {
210 | 'type': 'token',
211 | 'scheme': scheme,
212 | 'token': token
213 | };
214 | $('#selected-authentication').text('token');
215 | $('#auth-control').children().removeClass('active');
216 | $('#auth-control').find("[data-auth='token']").addClass('active');
217 | $('#auth_token_modal').modal('hide');
218 | });
219 |
220 | // Authentication: basic
221 | $('form.authentication-basic-form').submit(function(event) {
222 | event.preventDefault();
223 | const form = $(this).closest("form");
224 | const username = form.find('input#username').val();
225 | const password = form.find('input#password').val();
226 | window.auth = {
227 | 'type': 'basic',
228 | 'username': username,
229 | 'password': password
230 | };
231 | $('#selected-authentication').text('basic');
232 | $('#auth-control').children().removeClass('active');
233 | $('#auth-control').find("[data-auth='basic']").addClass('active');
234 | $('#auth_basic_modal').modal('hide');
235 | });
236 |
237 | // Authentication: session
238 | $('form.authentication-session-form').submit(function(event) {
239 | event.preventDefault();
240 | window.auth = {
241 | 'type': 'session',
242 | };
243 | $('#selected-authentication').text('session');
244 | $('#auth-control').children().removeClass('active');
245 | $('#auth-control').find("[data-auth='session']").addClass('active');
246 | $('#auth_session_modal').modal('hide');
247 | });
248 |
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/js/base.js:
--------------------------------------------------------------------------------
1 | function getSearchTerm()
2 | {
3 | var sPageURL = window.location.search.substring(1);
4 | var sURLVariables = sPageURL.split('&');
5 | for (var i = 0; i < sURLVariables.length; i++)
6 | {
7 | var sParameterName = sURLVariables[i].split('=');
8 | if (sParameterName[0] == 'q')
9 | {
10 | return sParameterName[1];
11 | }
12 | }
13 | }
14 |
15 | $(document).ready(function() {
16 |
17 | var search_term = getSearchTerm(),
18 | $search_modal = $('#mkdocs_search_modal');
19 |
20 | if(search_term){
21 | $search_modal.modal();
22 | }
23 |
24 | // make sure search input gets autofocus everytime modal opens.
25 | $search_modal.on('shown.bs.modal', function () {
26 | $search_modal.find('#mkdocs-search-query').focus();
27 | });
28 |
29 | // Highlight.js
30 | hljs.initHighlightingOnLoad();
31 | $('table').addClass('table table-striped table-hover');
32 | });
33 |
34 |
35 | $('body').scrollspy({
36 | target: '.bs-sidebar',
37 | });
38 |
39 | /* Prevent disabled links from causing a page reload */
40 | $("li.disabled a").click(function() {
41 | event.preventDefault();
42 | });
43 |
--------------------------------------------------------------------------------
/static_cdn/rest_framework/docs/js/jquery.json-view.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jquery.json-view - jQuery collapsible JSON plugin
3 | * @version v1.0.0
4 | * @link http://github.com/bazh/jquery.json-view
5 | * @license MIT
6 | */
7 | !function(e){"use strict";var n=function(n){var a=e("",{"class":"collapser",on:{click:function(){var n=e(this);n.toggleClass("collapsed");var a=n.parent().children(".block"),p=a.children("ul");n.hasClass("collapsed")?(p.hide(),a.children(".dots, .comments").show()):(p.show(),a.children(".dots, .comments").hide())}}});return n&&a.addClass("collapsed"),a},a=function(a,p){var t=e.extend({},{nl2br:!0},p),r=function(e){return e.toString()?e.toString().replace(/&/g,"&").replace(/"/g,""").replace(//g,">"):""},s=function(n,a){return e("",{"class":a,html:r(n)})},l=function(a,p){switch(e.type(a)){case"object":p||(p=0);var c=e("",{"class":"block"}),d=Object.keys(a).length;if(!d)return c.append(s("{","b")).append(" ").append(s("}","b"));c.append(s("{","b"));var i=e("
",{"class":"obj collapsible level"+p});return e.each(a,function(a,t){d--;var r=e("").append(s('"',"q")).append(a).append(s('"',"q")).append(": ").append(l(t,p+1));-1===["object","array"].indexOf(e.type(t))||e.isEmptyObject(t)||r.prepend(n()),d>0&&r.append(","),i.append(r)}),c.append(i),c.append(s("...","dots")),c.append(s("}","b")),c.append(1===Object.keys(a).length?s("// 1 item","comments"):s("// "+Object.keys(a).length+" items","comments")),c;case"array":p||(p=0);var d=a.length,c=e("",{"class":"block"});if(!d)return c.append(s("[","b")).append(" ").append(s("]","b"));c.append(s("[","b"));var i=e("
",{"class":"obj collapsible level"+p});return e.each(a,function(a,t){d--;var r=e("").append(l(t,p+1));-1===["object","array"].indexOf(e.type(t))||e.isEmptyObject(t)||r.prepend(n()),d>0&&r.append(","),i.append(r)}),c.append(i),c.append(s("...","dots")),c.append(s("]","b")),c.append(1===a.length?s("// 1 item","comments"):s("// "+a.length+" items","comments")),c;case"string":if(a=r(a),/^(http|https|file):\/\/[^\s]+$/i.test(a))return e("").append(s('"',"q")).append(e("",{href:a,text:a})).append(s('"',"q"));if(t.nl2br){var o=/\n/g;o.test(a)&&(a=(a+"").replace(o,"
"))}var u=e("",{"class":"str"}).html(a);return e("").append(s('"',"q")).append(u).append(s('"',"q"));case"number":return s(a.toString(),"num");case"undefined":return s("undefined","undef");case"null":return s("null","null");case"boolean":return s(a?"true":"false","bool")}};return l(a)};return e.fn.jsonView=function(n,p){var t=e(this);if(p=e.extend({},{nl2br:!0},p),"string"==typeof n)try{n=JSON.parse(n)}catch(r){}return t.append(e("",{"class":"json-view"}).append(a(n,p))),t}}(jQuery);
--------------------------------------------------------------------------------
/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/static_cdn/rest_framework/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/static_cdn/rest_framework/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/static_cdn/rest_framework/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingforentrepreneurs/Django-Likes/6d087d1214b3c00e8454532c10cfae19162c6032/static_cdn/rest_framework/img/grid.png
--------------------------------------------------------------------------------
/static_cdn/rest_framework/js/ajax-form.js:
--------------------------------------------------------------------------------
1 | function replaceDocument(docString) {
2 | var doc = document.open("text/html");
3 |
4 | doc.write(docString);
5 | doc.close();
6 | }
7 |
8 | function doAjaxSubmit(e) {
9 | var form = $(this);
10 | var btn = $(this.clk);
11 | var method = (
12 | btn.data('method') ||
13 | form.data('method') ||
14 | form.attr('method') || 'GET'
15 | ).toUpperCase();
16 |
17 | if (method === 'GET') {
18 | // GET requests can always use standard form submits.
19 | return;
20 | }
21 |
22 | var contentType =
23 | form.find('input[data-override="content-type"]').val() ||
24 | form.find('select[data-override="content-type"] option:selected').text();
25 |
26 | if (method === 'POST' && !contentType) {
27 | // POST requests can use standard form submits, unless we have
28 | // overridden the content type.
29 | return;
30 | }
31 |
32 | // At this point we need to make an AJAX form submission.
33 | e.preventDefault();
34 |
35 | var url = form.attr('action');
36 | var data;
37 |
38 | if (contentType) {
39 | data = form.find('[data-override="content"]').val() || ''
40 | } else {
41 | contentType = form.attr('enctype') || form.attr('encoding')
42 |
43 | if (contentType === 'multipart/form-data') {
44 | if (!window.FormData) {
45 | alert('Your browser does not support AJAX multipart form submissions');
46 | return;
47 | }
48 |
49 | // Use the FormData API and allow the content type to be set automatically,
50 | // so it includes the boundary string.
51 | // See https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
52 | contentType = false;
53 | data = new FormData(form[0]);
54 | } else {
55 | contentType = 'application/x-www-form-urlencoded; charset=UTF-8'
56 | data = form.serialize();
57 | }
58 | }
59 |
60 | var ret = $.ajax({
61 | url: url,
62 | method: method,
63 | data: data,
64 | contentType: contentType,
65 | processData: false,
66 | headers: {
67 | 'Accept': 'text/html; q=1.0, */*'
68 | },
69 | });
70 |
71 | ret.always(function(data, textStatus, jqXHR) {
72 | if (textStatus != 'success') {
73 | jqXHR = data;
74 | }
75 |
76 | var responseContentType = jqXHR.getResponseHeader("content-type") || "";
77 |
78 | if (responseContentType.toLowerCase().indexOf('text/html') === 0) {
79 | replaceDocument(jqXHR.responseText);
80 |
81 | try {
82 | // Modify the location and scroll to top, as if after page load.
83 | history.replaceState({}, '', url);
84 | scroll(0, 0);
85 | } catch (err) {
86 | // History API not supported, so redirect.
87 | window.location = url;
88 | }
89 | } else {
90 | // Not HTML content. We can't open this directly, so redirect.
91 | window.location = url;
92 | }
93 | });
94 |
95 | return ret;
96 | }
97 |
98 | function captureSubmittingElement(e) {
99 | var target = e.target;
100 | var form = this;
101 |
102 | form.clk = target;
103 | }
104 |
105 | $.fn.ajaxForm = function() {
106 | var options = {}
107 |
108 | return this
109 | .unbind('submit.form-plugin click.form-plugin')
110 | .bind('submit.form-plugin', options, doAjaxSubmit)
111 | .bind('click.form-plugin', options, captureSubmittingElement);
112 | };
113 |
--------------------------------------------------------------------------------
/static_cdn/rest_framework/js/csrf.js:
--------------------------------------------------------------------------------
1 | function getCookie(name) {
2 | var cookieValue = null;
3 |
4 | if (document.cookie && document.cookie != '') {
5 | var cookies = document.cookie.split(';');
6 |
7 | for (var i = 0; i < cookies.length; i++) {
8 | var cookie = jQuery.trim(cookies[i]);
9 |
10 | // Does this cookie string begin with the name we want?
11 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
12 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
13 | break;
14 | }
15 | }
16 | }
17 |
18 | return cookieValue;
19 | }
20 |
21 | function csrfSafeMethod(method) {
22 | // these HTTP methods do not require CSRF protection
23 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
24 | }
25 |
26 | function sameOrigin(url) {
27 | // test that a given url is a same-origin URL
28 | // url could be relative or scheme relative or absolute
29 | var host = document.location.host; // host + port
30 | var protocol = document.location.protocol;
31 | var sr_origin = '//' + host;
32 | var origin = protocol + sr_origin;
33 |
34 | // Allow absolute or scheme relative URLs to same origin
35 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
36 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
37 | // or any other URL that isn't scheme relative or absolute i.e relative.
38 | !(/^(\/\/|http:|https:).*/.test(url));
39 | }
40 |
41 | var csrftoken = getCookie(window.drf.csrfCookieName);
42 |
43 | $.ajaxSetup({
44 | beforeSend: function(xhr, settings) {
45 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
46 | // Send the token to same-origin, relative URLs only.
47 | // Send the token only if the method warrants CSRF protection
48 | // Using the CSRFToken value acquired earlier
49 | xhr.setRequestHeader(window.drf.csrfHeaderName, csrftoken);
50 | }
51 | }
52 | });
53 |
--------------------------------------------------------------------------------
/static_cdn/rest_framework/js/default.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | // JSON highlighting.
3 | prettyPrint();
4 |
5 | // Bootstrap tooltips.
6 | $('.js-tooltip').tooltip({
7 | delay: 1000,
8 | container: 'body'
9 | });
10 |
11 | // Deal with rounded tab styling after tab clicks.
12 | $('a[data-toggle="tab"]:first').on('shown', function(e) {
13 | $(e.target).parents('.tabbable').addClass('first-tab-active');
14 | });
15 |
16 | $('a[data-toggle="tab"]:not(:first)').on('shown', function(e) {
17 | $(e.target).parents('.tabbable').removeClass('first-tab-active');
18 | });
19 |
20 | $('a[data-toggle="tab"]').click(function() {
21 | document.cookie = "tabstyle=" + this.name + "; path=/";
22 | });
23 |
24 | // Store tab preference in cookies & display appropriate tab on load.
25 | var selectedTab = null;
26 | var selectedTabName = getCookie('tabstyle');
27 |
28 | if (selectedTabName) {
29 | selectedTabName = selectedTabName.replace(/[^a-z-]/g, '');
30 | }
31 |
32 | if (selectedTabName) {
33 | selectedTab = $('.form-switcher a[name=' + selectedTabName + ']');
34 | }
35 |
36 | if (selectedTab && selectedTab.length > 0) {
37 | // Display whichever tab is selected.
38 | selectedTab.tab('show');
39 | } else {
40 | // If no tab selected, display rightmost tab.
41 | $('.form-switcher a:first').tab('show');
42 | }
43 |
44 | $(window).load(function() {
45 | $('#errorModal').modal('show');
46 | });
47 | });
48 |
--------------------------------------------------------------------------------