├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── Procfile ├── README.rst ├── app.json ├── django_blog_it ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── create_blog_user.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── sitemaps.py ├── static │ └── django_blog_it │ │ ├── css │ │ └── main.css │ │ ├── js │ │ ├── grapejs.js │ │ └── grapesjs-preset-webpage.min.js │ │ └── keep ├── templates │ └── django_blog_it │ │ ├── admin │ │ ├── base.html │ │ ├── blog_category_create.html │ │ ├── blog_category_edit.html │ │ ├── blog_category_list.html │ │ ├── blog_edit.html │ │ ├── blog_list.html │ │ ├── blog_new.html │ │ ├── ckeditor.html │ │ ├── dashboard.html │ │ ├── grape_js.html │ │ ├── preview.html │ │ └── user_list.html │ │ ├── blog │ │ ├── base.html │ │ └── blog.html │ │ └── page │ │ ├── base.html │ │ └── page.html ├── templatetags │ ├── __init__.py │ └── role_tags.py ├── tests.py ├── urls.py └── views.py ├── docs ├── Makefile ├── __init__.py └── source │ ├── conf.py │ └── index.rst ├── loaders.py ├── requirements.txt ├── sandbox ├── blog_test │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py └── requirements.txt ├── setup.py └── test_runner.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | 3env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | .idea/ 60 | db.sqlite3 61 | media/ 62 | *~ 63 | *.swp 64 | *.swo 65 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6.3" 4 | 5 | #Django & Database 6 | addons: 7 | #Travis CI does not yet support Postgres 10.1 8 | #See https://github.com/travis-ci/travis-ci/issues/8537 9 | #postgresql: "10.1" 10 | postgresql: "9.4" 11 | 12 | sudo: false 13 | 14 | env: 15 | -DJANGO=2.1 DB=mysql 16 | -DJANGO=2.1 DB=sqlite3 17 | -DJANGO=2.1 DB=postgresql 18 | 19 | install: 20 | - pip install -r requirements.txt 21 | - pip install coveralls 22 | 23 | 24 | #mysql db creation 25 | before_script: 26 | - sleep 15 27 | - psql -c 'create database test;' -U postgres 28 | 29 | # command to run tests 30 | script: 31 | - python test_runner.py test 32 | - coverage run --source=django_blog_it.posts,django_blog_it.django_blog_it test_runner.py test 33 | 34 | after_success: 35 | coveralls 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 MicroPyramid 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 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | recursive-include django_blog_it/templates * 3 | recursive-include django_blog_it/static * 4 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: sh -c 'cd sandbox && gunicorn blog_test.wsgi && pip install -r requirements.txt' -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | django-blog-it 2 | -------------- 3 | 4 | .. image:: https://readthedocs.org/projects/django-blog-it/badge/?version=latest 5 | :target: http://django-blog-it.readthedocs.org/en/latest/?badge=latest 6 | 7 | .. image:: https://img.shields.io/pypi/v/django-blog-it.svg 8 | :target: https://pypi.python.org/pypi/django-blog-it 9 | :alt: Latest Release 10 | 11 | .. image:: https://travis-ci.org/MicroPyramid/django-blog-it.svg?branch=master 12 | :target: https://travis-ci.org/MicroPyramid/django-blog-it 13 | 14 | .. image:: https://coveralls.io/repos/github/MicroPyramid/django-blog-it/badge.svg?branch=master 15 | :target: https://coveralls.io/github/MicroPyramid/django-blog-it?branch=master 16 | 17 | .. image:: https://img.shields.io/github/license/micropyramid/django-blog-it.svg 18 | :target: https://pypi.python.org/pypi/django-blog-it/ 19 | 20 | .. image:: https://landscape.io/github/MicroPyramid/django-blog-it/master/landscape.svg?style=flat 21 | :target: https://landscape.io/github/MicroPyramid/django-blog-it/master 22 | :alt: Code Health 23 | 24 | .. image:: https://api.codacy.com/project/badge/Grade/7eab875ee3b943d1a3a443f9b5d274b9 25 | :alt: Codacy Badge 26 | :target: https://app.codacy.com/app/ashwin/django-blog-it?utm_source=github.com&utm_medium=referral&utm_content=MicroPyramid/django-blog-it&utm_campaign=badger 27 | 28 | 29 | Simple blog package developed with Django. 30 | 31 | Features: 32 | 33 | - Dynamic blog articles 34 | - Blog pages 35 | - Contact us page (configurable) 36 | - google analytics 37 | - SEO compliant 38 | 39 | Installation 40 | -------------- 41 | 42 | 1. Install django-blog-it using the following command:: 43 | 44 | pip install django-blog-it 45 | 46 | 47 | (or) 48 | 49 | git clone git://github.com/micropyramid/django-blog-it.git 50 | 51 | cd django-blog-it 52 | 53 | python setup.py install 54 | 55 | 2. Add app name in settings.py:: 56 | 57 | INSTALLED_APPS = [ 58 | '..................', 59 | 'simple_pagination', 60 | 'django_blog_it.django_blog_it', 61 | '..................' 62 | ] 63 | 64 | 3. Include the django_blog_it urls in your urls.py:: 65 | 66 | from django.conf.urls import include 67 | 68 | urlpatterns = [ 69 | url(r'^admin/', admin.site.urls), 70 | url(r'', include('django_blog_it.urls')), 71 | ] 72 | 73 | 4. After installing/cloning this, add the following settings in the virtual env/bin/activate file to start discussions on blog articles :: 74 | 75 | You can create your disqus account at https://disqus.com/profile/login/ 76 | 77 | # Disquss details 78 | 79 | DISQUSSHORTNAME="Your Disquss Short Name" 80 | 81 | export DISQUSSHORTNAME 82 | 83 | # google api key for short url 84 | 85 | API_KEY="google api key" 86 | 87 | export API_KEY 88 | 89 | # google captcha 90 | 91 | GOOGLE_CAPTCHA_SITE_KEY="Site key" 92 | 93 | export GOOGLE_CAPTCHA_SITE_KEY 94 | 95 | GOOGLE_CAPTCHA_SECRET_KEY="Secret key" 96 | 97 | export GOOGLE_CAPTCHA_SECRET_KEY 98 | 99 | # Google Analytics Account 100 | 101 | GOOGLE_ANALYTICS_ID="UA-123456789" 102 | 103 | export GOOGLE_ANALYTICS_ID 104 | 105 | # Google Login 106 | 107 | GP_CLIENT_ID="google client id" 108 | 109 | export GP_CLIENT_ID 110 | 111 | GP_CLIENT_SECRET="secret key" 112 | 113 | export GP_CLIENT_SECRET 114 | 115 | # Facebook Login 116 | 117 | FB_APP_ID="facebook app id" 118 | 119 | export FB_APP_ID 120 | 121 | FB_SECRET="023df180c6d868e76a02aec17134c843" 122 | 123 | export FB_SECRET 124 | 125 | # Default E-mail 126 | 127 | DEFAULT_EMAIL="noreply@djangoblogit.com" 128 | 129 | export DEFAULT_EMAIL 130 | 131 | 132 | 5. If you cloned the package from git use virtualenv to install requirements:: 133 | 134 | pip install -r requirements.txt 135 | 136 | You can try it by hosting on your own or deploy to Heroku with a button click. 137 | 138 | Deploy To Heroku: 139 | 140 | .. image:: https://www.herokucdn.com/deploy/button.svg 141 | :target: https://heroku.com/deploy?template=https://github.com/MicroPyramid/django-blog-it 142 | 143 | Visit our Django web development page `Here`_ 144 | 145 | We welcome your feedback and support, raise github ticket if you want to report a bug. Need new features? `Contact us here`_ 146 | 147 | .. _contact us here: https://micropyramid.com/contact-us/ 148 | .. _Here: https://micropyramid.com/django-ecommerce-development/ 149 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Django Blog", 3 | "description": "Self-hosted blog app", 4 | "repository": "https://github.com/MicroPyramid/django-blog-it", 5 | "scripts": { 6 | "postdeploy": "python sandbox/manage.py makemigrations && python sandbox/manage.py migrate && echo \"from django.contrib.auth.models import User; User.objects.create_superuser('Admin001', 'admin001@djangoblogit.com', 'admin001')\" | python sandbox/manage.py shell" 7 | }, 8 | "env": { 9 | "API_KEY": { 10 | "description": "micro url ", 11 | "value": "AIzaSyANPi_ULytUHdU4UKtlVmb_Jo1_N29IwTE" 12 | }, 13 | "GOOGLE_CAPTCHA_SITE_KEY": { 14 | "description": "google captcha site key", 15 | "value": "6LczhSUTAAAAAEGXEzXenG9LTpRgWj5kZ5HKUsys" 16 | }, 17 | "GOOGLE_CAPTCHA_SECRET_KEY": { 18 | "description": "google captcha secret key", 19 | "value": "6LczhSUTAAAAAPcdeBD4YSJfijBsoI1T2rNiXB-D" 20 | }, 21 | "GOOGLE_ANALYTICS_ID": { 22 | "description": "google analytics Id", 23 | "value": "UA-1232342342" 24 | }, 25 | "GP_CLIENT_ID": { 26 | "description": "Google client login Id", 27 | "value": "51531256941-jbi840coio2vg8q1mht5bmuq002p2kt8.apps.googleusercontent.com" 28 | }, 29 | "GP_CLIENT_SECRET": { 30 | "description": "Google client secret key", 31 | "value": "757UNcQ3Py03QVFMm4Wg5tsE" 32 | }, 33 | "FB_APP_ID": { 34 | "description": "Facebook app id", 35 | "value": "1578441772473570" 36 | }, 37 | "FB_SECRET": { 38 | "description": "Facebook secret key", 39 | "value": "023df180c6d868e76a02aec17134c843" 40 | }, 41 | "DEFAULT_EMAIL": { 42 | "description": "default email", 43 | "value": "noreply@djangoblogit.com" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /django_blog_it/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicroPyramid/django-blog-it/35596ce201b5cbf037e958708788581039a16346/django_blog_it/__init__.py -------------------------------------------------------------------------------- /django_blog_it/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Article, Category, Tag, BlogUser 3 | 4 | 5 | # class BlogAdmin(admin.ModelAdmin): 6 | # list_display = ['title','category','tags','content'] 7 | # list_filter=('status','updated_on') 8 | # ordering=['publish'] 9 | 10 | # class CategoryAdmin(admin.ModelAdmin): 11 | # list_display = ('name', 'slug', 'description', 'is_active') 12 | 13 | # class TagsAdmin(admin.ModelAdmin): 14 | # list_display = ('name', 'slug') 15 | 16 | admin.site.register(BlogUser) 17 | admin.site.register(Article) 18 | admin.site.register(Category) 19 | admin.site.register(Tag) 20 | -------------------------------------------------------------------------------- /django_blog_it/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class RadminConfig(AppConfig): 5 | name = 'radmin' 6 | -------------------------------------------------------------------------------- /django_blog_it/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import Category, Article, Tag 3 | from django.template.defaultfilters import slugify 4 | 5 | 6 | class ArticleForm(forms.ModelForm): 7 | tags = forms.CharField(label="Tags", max_length=300, required=False) 8 | 9 | class Meta: 10 | model = Article 11 | exclude = ('created_by', 'tags', 'created_on', 'updated_on') 12 | 13 | def __init__(self, *args, **kwargs): 14 | self.user_role = kwargs.pop('user_role', None) 15 | self.type = kwargs.pop('type', None) 16 | super(ArticleForm, self).__init__(*args, **kwargs) 17 | self.fields['content'].required = False 18 | 19 | def clean_status(self): 20 | if self.user_role == "Author": 21 | raise forms.ValidationError( 22 | "Admin and Publisher can change status only.") 23 | return self.cleaned_data.get("status") 24 | 25 | 26 | class CategoryForm(forms.ModelForm): 27 | 28 | class Meta: 29 | model = Category 30 | exclude = ('slug', 'created_by') 31 | 32 | def clean_name(self): 33 | if not self.instance.id: 34 | if Category.objects.filter( 35 | slug=slugify(self.cleaned_data['name'])).exists(): 36 | raise forms.ValidationError( 37 | 'Category with this Name already exists.') 38 | else: 39 | if Category.objects.filter( 40 | name__icontains=self.cleaned_data['name']).exclude( 41 | id=self.instance.id): 42 | raise forms.ValidationError( 43 | 'Category with this Name already exists.') 44 | 45 | return self.cleaned_data['name'] 46 | 47 | def __init__(self, *args, **kwargs): 48 | self.request = kwargs.pop('request', None) 49 | super(CategoryForm, self).__init__(*args, **kwargs) 50 | 51 | for field in iter(self.fields): 52 | if max(enumerate(iter(self.fields)))[0] != field: 53 | self.fields[field].widget.attrs.update({ 54 | 'class': 'form-control', 55 | "placeholder": "Please enter your Category " + field.capitalize() 56 | }) 57 | -------------------------------------------------------------------------------- /django_blog_it/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicroPyramid/django-blog-it/35596ce201b5cbf037e958708788581039a16346/django_blog_it/management/__init__.py -------------------------------------------------------------------------------- /django_blog_it/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicroPyramid/django-blog-it/35596ce201b5cbf037e958708788581039a16346/django_blog_it/management/commands/__init__.py -------------------------------------------------------------------------------- /django_blog_it/management/commands/create_blog_user.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django_blog_it.models import BlogUser 3 | from django.core.management.base import BaseCommand 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'Create Blog User' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument('username', nargs='+', type=str, help='User ID') 11 | 12 | def handle(self, *args, **kwargs): 13 | username = kwargs["username"][0] 14 | if username: 15 | user = get_user_model().objects.filter(username=kwargs["username"][0]).first() 16 | if user: 17 | if not BlogUser.objects.filter(user_id=user, role="blog_admin").exists(): 18 | BlogUser.objects.create(user=user, role="blog_admin") 19 | print("BlogUser Created Successfully") 20 | -------------------------------------------------------------------------------- /django_blog_it/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-02-02 11:53 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Article', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('title', models.CharField(max_length=100, unique=True)), 22 | ('slug', models.SlugField(max_length=100, unique=True)), 23 | ('created_on', models.DateTimeField(auto_now_add=True)), 24 | ('updated_on', models.DateField(auto_now=True)), 25 | ('content', models.TextField()), 26 | ('status', models.CharField(choices=[('Drafted', 'Drafted'), ('Published', 'Published'), ('Trashed', 'Trashed'), ('Review', 'Review')], default='Drafted', max_length=10)), 27 | ('publish_on', models.DateField()), 28 | ('meta_description', models.TextField(blank=True, max_length=160, null=True)), 29 | ('meta_keywords', models.TextField(blank=True, max_length=255, null=True)), 30 | ('meta_author', models.TextField(blank=True, max_length=255, null=True)), 31 | ('is_page', models.BooleanField(default=False)), 32 | ], 33 | ), 34 | migrations.CreateModel( 35 | name='Tag', 36 | fields=[ 37 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 38 | ('name', models.CharField(max_length=20, unique=True)), 39 | ('slug', models.CharField(max_length=20, unique=True)), 40 | ], 41 | ), 42 | migrations.CreateModel( 43 | name='History', 44 | fields=[ 45 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 46 | ('content', models.CharField(max_length=200)), 47 | ('created_at', models.DateTimeField(auto_now_add=True)), 48 | ('is_page', models.BooleanField(default=False)), 49 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='history', to='django_blog_it.article')), 50 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 51 | ], 52 | ), 53 | migrations.CreateModel( 54 | name='Category', 55 | fields=[ 56 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 57 | ('name', models.CharField(max_length=20, unique=True)), 58 | ('slug', models.CharField(max_length=20, unique=True)), 59 | ('description', models.CharField(max_length=500)), 60 | ('is_active', models.BooleanField(default=False)), 61 | ('meta_description', models.TextField(blank=True, max_length=160, null=True)), 62 | ('meta_keywords', models.TextField(blank=True, max_length=255, null=True)), 63 | ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 64 | ], 65 | ), 66 | migrations.CreateModel( 67 | name='BlogUser', 68 | fields=[ 69 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 70 | ('role', models.CharField(choices=[('Blog Admin', 'blog_admin'), ('Blog Publisher', 'blog_publisher'), ('Blog Author', 'blog_author')], default='blog_author', max_length=200)), 71 | ('is_active', models.BooleanField(default=True)), 72 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_roles', to=settings.AUTH_USER_MODEL)), 73 | ], 74 | ), 75 | migrations.AddField( 76 | model_name='article', 77 | name='category', 78 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_blog_it.category'), 79 | ), 80 | migrations.AddField( 81 | model_name='article', 82 | name='created_by', 83 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 84 | ), 85 | migrations.AddField( 86 | model_name='article', 87 | name='tags', 88 | field=models.ManyToManyField(related_name='related_posts', to='django_blog_it.Tag'), 89 | ), 90 | ] 91 | -------------------------------------------------------------------------------- /django_blog_it/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicroPyramid/django-blog-it/35596ce201b5cbf037e958708788581039a16346/django_blog_it/migrations/__init__.py -------------------------------------------------------------------------------- /django_blog_it/models.py: -------------------------------------------------------------------------------- 1 | import arrow 2 | from django.db import models 3 | from django.template.defaultfilters import slugify 4 | from django.conf import settings 5 | from django.core.exceptions import ObjectDoesNotExist 6 | 7 | # Create your models here. 8 | 9 | BLOG_STATUS_CHOICE = ( 10 | ('Drafted', 'Drafted'), 11 | ('Published', 'Published'), 12 | ('Trashed', 'Trashed'), 13 | ('Review', 'Review') 14 | ) 15 | 16 | ROLE_CHOICE = ( 17 | ('Blog Admin', 'blog_admin'), 18 | ('Blog Publisher', 'blog_publisher'), 19 | ('Blog Author', 'blog_author'), 20 | ) 21 | 22 | 23 | class BlogUser(models.Model): 24 | user = models.ForeignKey(settings.AUTH_USER_MODEL, 25 | on_delete=models.CASCADE, related_name="user_roles") 26 | role = models.CharField(max_length=200, choices=ROLE_CHOICE, 27 | default='blog_author') 28 | is_active = models.BooleanField(default=True) 29 | 30 | 31 | class Category(models.Model): 32 | name = models.CharField(max_length=20, unique=True) 33 | slug = models.CharField(max_length=20, unique=True) 34 | description = models.CharField(max_length=500) 35 | is_active = models.BooleanField(default=False) 36 | meta_description = models.TextField(max_length=160, null=True, blank=True) 37 | meta_keywords = models.TextField(max_length=255, null=True, blank=True) 38 | created_by = models.ForeignKey( 39 | settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 40 | 41 | def save(self, *args, **kwargs): 42 | self.slug = slugify(self.name) 43 | super(Category, self).save(*args, **kwargs) 44 | 45 | def __str__(self): 46 | return self.name 47 | 48 | def category_posts(self): 49 | return Article.objects.filter(category=self).count() 50 | 51 | 52 | class Tag(models.Model): 53 | name = models.CharField(max_length=20, unique=True) 54 | slug = models.CharField(max_length=20, unique=True) 55 | 56 | def save(self, *args, **kwargs): 57 | tempslug = slugify(self.name) 58 | if self.id: 59 | tag = Tag.objects.get(pk=self.id) 60 | if tag.name != self.name: 61 | self.slug = create_tag_slug(tempslug) 62 | else: 63 | self.slug = create_tag_slug(tempslug) 64 | super(Tag, self).save(*args, **kwargs) 65 | 66 | def __str__(self): 67 | return self.name 68 | 69 | 70 | def create_tag_slug(tempslug): 71 | slugcount = 0 72 | while True: 73 | try: 74 | Tag.objects.get(slug=tempslug) 75 | slugcount += 1 76 | tempslug = tempslug + '-' + str(slugcount) 77 | except ObjectDoesNotExist: 78 | return tempslug 79 | 80 | 81 | class Article(models.Model): 82 | title = models.CharField(max_length=100, unique=True) 83 | slug = models.SlugField(max_length=100, unique=True) 84 | created_on = models.DateTimeField(auto_now_add=True) 85 | updated_on = models.DateField(auto_now=True) 86 | created_by = models.ForeignKey( 87 | settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 88 | content = models.TextField() 89 | category = models.ForeignKey( 90 | Category, on_delete=models.CASCADE, null=True, blank=True) 91 | tags = models.ManyToManyField(Tag, related_name='related_posts') 92 | status = models.CharField( 93 | max_length=10, choices=BLOG_STATUS_CHOICE, default='Drafted') 94 | publish_on = models.DateField() 95 | meta_description = models.TextField(max_length=160, null=True, blank=True) 96 | meta_keywords = models.TextField(max_length=255, null=True, blank=True) 97 | meta_author = models.TextField(max_length=255, null=True, blank=True) 98 | is_page = models.BooleanField(default=False) 99 | 100 | def __str__(self): 101 | return self.title 102 | 103 | def create_activity(self, user, content): 104 | return History.objects.create( 105 | user=user, post=self, content=content 106 | ) 107 | 108 | def create_activity_instance(self, user, content): 109 | return History( 110 | user=user, post=self, content=content 111 | ) 112 | 113 | @property 114 | def created_on_arrow(self): 115 | return arrow.get(self.created_on).humanize() 116 | 117 | @property 118 | def published_on_arrow(self): 119 | return arrow.get(self.publish_on).humanize() 120 | 121 | 122 | def create_slug(tempslug): 123 | slugcount = 0 124 | while True: 125 | try: 126 | Article.objects.get(slug=tempslug) 127 | slugcount += 1 128 | tempslug = tempslug + '-' + str(slugcount) 129 | except ObjectDoesNotExist: 130 | return tempslug 131 | 132 | 133 | class History(models.Model): 134 | user = models.ForeignKey(settings.AUTH_USER_MODEL, 135 | on_delete=models.CASCADE) 136 | post = models.ForeignKey( 137 | Article, related_name='history', on_delete=models.CASCADE) 138 | content = models.CharField(max_length=200) 139 | created_at = models.DateTimeField(auto_now_add=True) 140 | is_page = models.BooleanField(default=False) 141 | 142 | def __str__(self): 143 | return '{username} {content} {blog_title}'.format( 144 | username=str(self.user.get_username()), 145 | content=str(self.content), 146 | blog_title=str(self.post.title) 147 | ) 148 | -------------------------------------------------------------------------------- /django_blog_it/sitemaps.py: -------------------------------------------------------------------------------- 1 | from django_blog_it.models import Article 2 | from django.http.response import HttpResponse 3 | from django.conf import settings 4 | from django.shortcuts import reverse 5 | 6 | def sitemap_xml(request, **kwargs): 7 | 8 | xml = ''' 9 | ''' 10 | 11 | xml = xml + '' + settings.DJANGO_BLOG_IT_DOMAIN + '/' 12 | pages = Article.objects.filter(is_page=True) 13 | for page in pages: 14 | xml = xml + '' + settings.DJANGO_BLOG_IT_DOMAIN + \ 15 | reverse('django_blog_it:page_detail', kwargs={ 16 | 'slug': page.slug}) + '' 17 | 18 | blogs = Article.objects.exclude(is_page=True) 19 | for blog in blogs: 20 | xml = xml + '' + settings.DJANGO_BLOG_IT_DOMAIN + \ 21 | reverse('django_blog_it:blog_detail', kwargs={ 22 | 'slug': blog.slug}) + '' 23 | 24 | xml = xml + '' + settings.DJANGO_BLOG_IT_DOMAIN + '/sitemap/' 25 | xml = xml + '' + settings.DJANGO_BLOG_IT_DOMAIN + '/blog/' 26 | xml = xml + '' 27 | 28 | return HttpResponse(xml, content_type="text/xml") 29 | -------------------------------------------------------------------------------- /django_blog_it/static/django_blog_it/css/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; } 3 | 4 | body { 5 | font-family: 'Jost', sans-serif; 6 | font-size: 16px; 7 | color: #4a4a4a; 8 | padding-top: 75px; 9 | height: 100%; 10 | background: #ededed; } 11 | 12 | .no_margin_top { 13 | margin-top: 0; } 14 | 15 | .form-error { 16 | color: red; } 17 | 18 | header { 19 | background: #141a2b; 20 | padding: 0 5rem; } 21 | header nav { 22 | background-color: #141a2b !important; 23 | padding: 0 !important; } 24 | header nav .navbar-brand { 25 | margin-right: 30px; 26 | color: #fff; } 27 | header nav .navbar-brand .refactored_logo { 28 | width: 190px; 29 | height: auto; } 30 | header nav .navbar-nav li a.nav-link { 31 | font-size: 1rem; 32 | color: #fff; 33 | padding: 20px 6px !important; 34 | margin: 0 10px; } 35 | header nav .navbar-nav li a.nav-link:hover { 36 | color: #F5A623; } 37 | header nav .navbar-nav li:first-child { 38 | margin-left: 10px; } 39 | header nav .navbar-nav li.active a { 40 | border-bottom: 3px solid #6f5e53; } 41 | header nav .navbar-nav.navbar-right { 42 | border-left: 0; } 43 | header nav .navbar-nav.navbar-right .nav-item { 44 | margin-left: 25px; } 45 | header nav .navbar-nav.navbar-right .nav-item .dropdown-toggle { 46 | padding: 20px 6px !important; 47 | color: #fff; 48 | font-size: 16px; 49 | text-transform: none; 50 | font-weight: 400; 51 | letter-spacing: 0; 52 | display: inline-block; } 53 | header nav .navbar-nav.navbar-right .nav-item .dropdown-toggle:after { 54 | display: none; } 55 | header nav .navbar-nav.navbar-right .nav-item .dropdown-menu { 56 | position: absolute; 57 | padding: 0; 58 | background: #6d6ce8; 59 | width: 200px; 60 | right: 0px; 61 | top: 62px; 62 | border: none; 63 | border-radius: 0; } 64 | header nav .navbar-nav.navbar-right .nav-item .dropdown-menu .dropdown-item { 65 | color: #fff; 66 | padding: 8px 24px !important; 67 | display: inline-block; 68 | font-weight: 400; 69 | font-size: 1rem; } 70 | header nav .navbar-nav.navbar-right .nav-item .dropdown-menu .dropdown-item i { 71 | display: inline-block; 72 | margin-right: 10px; } 73 | header nav .navbar-nav.navbar-right .nav-item .dropdown-menu .dropdown-item:hover { 74 | background: #F5A623; 75 | color: #000; } 76 | header nav .navbar-nav.navbar-right .nav-item .refactored_nav_dropdown_right { 77 | width: initial; } 78 | header nav .navbar-nav.navbar-right .nav-item .refactored_nav_dropdown_right a:hover { 79 | color: #000; 80 | background: #F5A623; } 81 | header nav .navbar-nav.navbar-right .nav-item.show { 82 | background: #6d6ce8; } 83 | 84 | .primary_btn, .secondary_btn { 85 | border-radius: 3px; 86 | background: #F5A623; 87 | color: #fff; 88 | font-size: 1rem; 89 | display: inline-block; 90 | padding: 0.5rem 1rem; } 91 | .primary_btn i, .secondary_btn i { 92 | display: inline-block; 93 | margin-right: 0.4rem; } 94 | .primary_btn:hover, .secondary_btn:hover { 95 | background: #141a2b; 96 | color: #fff; 97 | text-decoration: none; } 98 | 99 | .secondary_btn { 100 | background: #8382eb; } 101 | .secondary_btn:hover { 102 | background: #6d6ce8; } 103 | 104 | .main_container { 105 | padding: 0 5rem; 106 | margin-top: 2rem; } 107 | .main_container .card { 108 | border: none; 109 | box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); } 110 | .main_container .card .card-header { 111 | background: #fff; 112 | font-size: 1.25rem; 113 | font-weight: 500; 114 | border-bottom: 1px solid #ececec; } 115 | .main_container .card .card-header .title { 116 | position: relative; 117 | top: 5px; } 118 | .main_container .card .form-inline .form-control { 119 | width: 325px; 120 | margin-right: 0 !important; } 121 | .main_container .card .form-inline .custom-select { 122 | position: relative; 123 | top: -1px; 124 | margin-top: 0 !important; } 125 | .main_container .card .table { 126 | margin-top: 2rem; } 127 | .main_container .card .table thead tr th { 128 | background: #f8f8f8; 129 | border: none; 130 | font-weight: 500; } 131 | .main_container .card .table tbody tr td { 132 | border: none; 133 | color: #4a4a4a; 134 | vertical-align: middle; 135 | border-bottom: 1px solid #ececec; } 136 | .main_container .card .table tbody tr td.status.publish i { 137 | color: #a665d5; } 138 | .main_container .card .table tbody tr td.status.draft i { 139 | color: #216ff8; } 140 | .main_container .card .table tbody tr td.status.reject i { 141 | color: #f6424c; } 142 | .main_container .card .table tbody tr td.status.review i { 143 | color: #40a335; } 144 | .main_container .card .table tbody tr td.actions a { 145 | background: #fff; 146 | color: #fff; 147 | border-radius: 2px; 148 | display: inline-block; 149 | text-align: center; 150 | width: 32px; 151 | height: 32px; 152 | padding-top: 5px; 153 | border: 1px solid #ddd; } 154 | .main_container .card .table tbody tr td.actions a.edit { 155 | color: #6ab73d; } 156 | .main_container .card .table tbody tr td.actions a.edit:hover { 157 | background: #6ab73d; 158 | border: 1px solid #6ab73d; } 159 | .main_container .card .table tbody tr td.actions a.delete { 160 | color: #ff5722; } 161 | .main_container .card .table tbody tr td.actions a.delete:hover { 162 | background: #ff5722; 163 | border: 1px solid #ff5722; } 164 | .main_container .card .table tbody tr td.actions a:hover { 165 | color: #fff; } 166 | 167 | .navigation { 168 | margin-top: 1.2rem; } 169 | .navigation .pagination li { 170 | margin-right: 3px; } 171 | .navigation .pagination li.active a { 172 | background: #6d6ce8; 173 | color: #fff; 174 | border: none; } 175 | .navigation .pagination li a { 176 | width: 32px; 177 | height: 32px; 178 | background: #fff; 179 | color: #4a4a4a; 180 | font-size: 1rem; 181 | padding: 0.35rem 0rem; 182 | border: none; 183 | text-align: center; 184 | border-radius: 2px !important; 185 | border: 1px solid #ececec; } 186 | .navigation .pagination li a.nav { 187 | width: 86px; } 188 | .navigation .pagination li a:hover { 189 | background: #6d6ce8; 190 | color: #fff; } 191 | 192 | .mt-1 { 193 | margin-top: 1rem !important; } 194 | 195 | .slug_checkbox { 196 | margin-top: 0.75rem; } 197 | 198 | bootstrap-tagsinput { 199 | background-color: #fff; 200 | border: 1px solid #ccc; 201 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 202 | display: inline-block; 203 | padding: 4px 6px; 204 | color: #555; 205 | vertical-align: middle; 206 | border-radius: 4px; 207 | max-width: 100%; 208 | line-height: 22px; 209 | cursor: text; } 210 | 211 | .bootstrap-tagsinput { 212 | display: block; 213 | width: 100%; 214 | height: calc(1.5em + .75rem + 2px); 215 | padding: .375rem .75rem; 216 | font-size: 1rem; 217 | font-weight: 400; 218 | line-height: 1.5; 219 | color: #495057; 220 | background-color: #fff; 221 | background-clip: padding-box; 222 | border: 1px solid #ced4da; 223 | border-radius: .25rem; 224 | transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; } 225 | .bootstrap-tagsinput input { 226 | border: none; 227 | box-shadow: none; 228 | outline: none; 229 | background-color: transparent; 230 | padding: 0 6px; 231 | margin: 0; 232 | width: auto; 233 | max-width: inherit; } 234 | .bootstrap-tagsinput input:focus { 235 | border: none; 236 | box-shadow: none; } 237 | .bootstrap-tagsinput .tag { 238 | margin-right: 2px; 239 | color: white; 240 | background: #6c6be8; 241 | border-radius: 2px; 242 | display: inline-block; 243 | padding: 0rem 0.5rem; } 244 | .bootstrap-tagsinput .tag [data-role="remove"] { 245 | margin-left: 8px; 246 | cursor: pointer; } 247 | .bootstrap-tagsinput .tag [data-role="remove"]:after { 248 | content: "x"; 249 | padding: 0px 2px; } 250 | .bootstrap-tagsinput .tag [data-role="remove"]:hover { 251 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } 252 | .bootstrap-tagsinput .tag [data-role="remove"]:hover:active { 253 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } 254 | 255 | .bootstrap-tagsinput.form-control input::-moz-placeholder { 256 | color: #777; 257 | opacity: 1; } 258 | .bootstrap-tagsinput.form-control input:-ms-input-placeholder { 259 | color: #777; } 260 | .bootstrap-tagsinput.form-control input::-webkit-input-placeholder { 261 | color: #777; } 262 | 263 | .blog-container .blog-item { 264 | margin-bottom: 30px; 265 | display: table; 266 | padding-bottom: 20px; } 267 | 268 | .blog-container .blog-item .blog-right { 269 | display: table-cell; 270 | vertical-align: top; } 271 | 272 | .blog-container .blog-item .blog-right .blog-title { 273 | display: block; 274 | margin-bottom: 10px; 275 | font-size: 1.8rem !important; 276 | color: #000; 277 | margin: 20px 0px; 278 | font-family: 'Lato',sans-serif; 279 | line-height: 2.2rem; } 280 | 281 | .blog-container .blog-item .blog-right .date { 282 | color: #4c4c4c; 283 | font-size: 0.9rem !important; 284 | margin: 20px 0px; } 285 | 286 | .blog-container .blog-item .blog-right p { 287 | line-height: 30px; 288 | font-size: 1.1rem; 289 | margin-bottom: 10px; 290 | font-weight: 400; } 291 | 292 | .blog-container .read_more { 293 | margin-top: 2rem; } 294 | 295 | .blog-container .read_more a { 296 | color: #000; 297 | font-size: 1rem; 298 | font-family: 'Muli',sans-serif; 299 | border: 2px solid #fde008; 300 | padding: 10px 20px; } 301 | 302 | #datepicker { 303 | width: 180px; 304 | margin: 0 20px 20px 0px; } 305 | 306 | #datepicker > span:hover { 307 | cursor: pointer; } 308 | 309 | .nav-tabs .nav-item { 310 | margin-bottom: -1px; } 311 | .nav-tabs .nav-item .nav-link { 312 | background: #909090; 313 | color: #fff; 314 | font-size: 0.9rem; 315 | font-weight: 600; } 316 | .nav-tabs .nav-item .nav-link.active { 317 | background: #141a2b; } 318 | 319 | /*# sourceMappingURL=main.css.map */ 320 | -------------------------------------------------------------------------------- /django_blog_it/static/django_blog_it/js/grapejs.js: -------------------------------------------------------------------------------- 1 | var editor = grapesjs.init({ 2 | height: '100%', 3 | showOffsets: 1, 4 | noticeOnUnload: 0, 5 | storageManager: { autoload: 0 }, 6 | container: '#gjs', 7 | fromElement: true, 8 | allowScripts: 1, 9 | dragMode: 'translate', 10 | plugins: ['gjs-preset-webpage', 11 | 'grapesjs-tabs', 12 | 'grapesjs-custom-code', 13 | 'grapesjs-touch', 14 | 'grapesjs-parser-postcss', 15 | 'grapesjs-tooltip', 16 | 'grapesjs-tui-image-editor', 17 | 'grapesjs-typed', 18 | 'grapesjs-style-bg', 19 | 'grapesjs-lory-slider', 20 | 'gjs-blocks-flexbox', 21 | 'gjs-plugin-ckeditor', 22 | 'grapesjs-style-bg', 23 | 'grapesjs-style-filter', 24 | 'grapesjs-style-gradient' 25 | ], 26 | pluginsOpts: { 27 | 'grapesjs-lory-slider': { 28 | sliderBlock: { 29 | category: 'Extra' 30 | } 31 | }, 32 | 'grapesjs-tabs': { 33 | tabsBlock: { 34 | category: 'Extra' 35 | } 36 | }, 37 | 'grapesjs-typed': { 38 | block: { 39 | category: 'Extra', 40 | content: { 41 | type: 'typed', 42 | 'type-speed': 40, 43 | strings: [ 44 | 'Text row one', 45 | 'Text row two', 46 | 'Text row three', 47 | ], 48 | } 49 | } 50 | } 51 | }, 52 | 53 | }); 54 | 55 | editor.Panels.addButton('options', 56 | [{ 57 | id: 'save-db', 58 | className: 'fa fa-floppy-o custom_save_button', 59 | command: 'save-db', 60 | attributes: { title: 'Save DB' } 61 | }] 62 | ); 63 | 64 | // Add the command 65 | editor.Commands.add('save-db', { 66 | run: function(editor, sender) { 67 | sender && sender.set('active', 0); // turn off the button 68 | editor.store(); 69 | 70 | var htmldata = editor.getHtml(); 71 | var cssdata = editor.getCss(); 72 | var csrftoken = $('meta[name="_token"]').attr('content'); 73 | var status = confirm("Are you sure you want save?") 74 | if(status){ 75 | $.ajax({ 76 | type: 'POST', 77 | url: window.location.href, 78 | data: { 79 | "csrfmiddlewaretoken": csrftoken, 80 | "html": htmldata, 81 | "css": cssdata 82 | }, 83 | success: function(data) { 84 | window.location.href = data.redirect_url; 85 | } 86 | }); 87 | } 88 | } 89 | }); 90 | 91 | function AddCustomBlock(template) { 92 | var blockManager = editor.BlockManager; 93 | block_id = "custom_block"; 94 | blockManager.add(block_id, { 95 | label: 'Simple block' , 96 | content: template , 97 | category: 'Custom Templates', 98 | render: ({ el, model}) => { 99 | const btn = document.createElement('button'); 100 | btn.innerHTML = 'Apply'; 101 | btn.addEventListener('click', () =>{ 102 | editor.runCommand('core:canvas-clear') 103 | // console.log(`${model.get('content')}`) 104 | editor.setComponents(`${model.get('content')}`) 105 | }) 106 | el.appendChild(btn); 107 | }, 108 | }); 109 | 110 | } 111 | 112 | function AppendExistingHtml(html) { 113 | editor.setComponents(html); 114 | } 115 | 116 | function MakeAjaxCall() { 117 | var csrftoken = $('meta[name="_token"]').attr('content'); 118 | var landing_url = $("#html").attr("landinpage-url"); 119 | var templates_url = $("#html").attr("templates-url"); 120 | // Append Existing Landing page 121 | $.ajax({ 122 | type: 'POST', 123 | url: landing_url, 124 | data: { 125 | "csrfmiddlewaretoken": csrftoken, 126 | }, 127 | success: function(data) { 128 | // console.log(data.html); 129 | AppendExistingHtml(data.html) 130 | } 131 | }) 132 | 133 | // Load Templates 134 | $.ajax({ 135 | type: 'POST', 136 | url: templates_url, 137 | data: { 138 | "csrfmiddlewaretoken": csrftoken, 139 | }, 140 | success: function(template) { 141 | AddCustomBlock(template); 142 | } 143 | }) 144 | } 145 | 146 | editor.load(res => 147 | MakeAjaxCall() 148 | ); -------------------------------------------------------------------------------- /django_blog_it/static/django_blog_it/keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Django-Blog-It 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 | 69 |
70 |
71 |
72 |
73 | {% block content %} 74 | {% endblock %} 75 | 76 | 77 | 78 | 79 | 80 | 81 | {% block script %} 82 | {% endblock %} 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/blog_category_create.html: -------------------------------------------------------------------------------- 1 | {%extends 'django_blog_it/admin/base.html' %} 2 | {% block content %} 3 |
4 |
5 |
6 |
7 |
8 |
9 | Create Catergory Blog View Blog Category List 10 |
11 |
12 |
13 |
14 |
15 | {% csrf_token %} 16 |
17 |
18 |
19 | 20 | 21 | {{form.name | striptags}} 22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 | {{form.description | striptags}} 35 |
36 |
37 |
38 |
39 |
40 |
41 | 42 | 43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 |
55 |
56 | 57 | 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | 70 | {% endblock %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/blog_category_edit.html: -------------------------------------------------------------------------------- 1 | {%extends 'django_blog_it/admin/base.html' %} 2 | {% block content %} 3 |
4 |
5 |
6 |
7 |
8 |
9 | Edit Catergory Blog View Blog Category List 10 |
11 |
12 |
13 |
14 |
15 | {% csrf_token %} 16 |
17 |
18 |
19 | 20 | 21 | {{form.name | striptags}} 22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 | {{form.description | striptags}} 35 |
36 |
37 |
38 |
39 |
40 |
41 | 42 | 43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 |
55 |
56 | 57 | 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | 70 | 71 | {% endblock %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/blog_category_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'django_blog_it/admin/base.html' %} 2 | {% block content %} 3 | 4 |
5 |
6 |
7 |
8 |
9 |
10 | Category List New Category 11 |
12 |
13 |
14 |
15 |
16 | 17 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {% for blog in blogdata %} 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | {% endfor %} 45 | 46 |
TitleAuthorMeta DescriptionMeta KeywordsActions
{{ blog.name }}{{ blog.created_by.username }}{{ blog.meta_description }}{{ blog.meta_keywords }}
47 |
48 |
49 |
50 |
51 | 70 |
71 |
72 |
73 |
74 | 75 | {% endblock %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/blog_edit.html: -------------------------------------------------------------------------------- 1 | {%extends 'django_blog_it/admin/base.html' %} 2 | {% load role_tags %} 3 | {% load static %} 4 | {% block content %} 5 |
6 |
7 |
8 |
9 |
10 | 14 |
15 |
16 |
17 |
18 | {% csrf_token %} 19 | 20 |
21 |
22 |
23 | 24 | 25 | {{form.title | striptags}} 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | {{form.slug | striptags}} 35 |
36 |
37 |
38 |
39 | 40 | 46 |
47 |
48 |
49 | 50 |
51 | 52 | 53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 | 61 |
62 |
63 |
64 |
65 | 66 | 67 |
68 |
69 |
70 |
71 | 72 | 73 |
74 |
75 |
76 |
77 |
78 |
79 | 80 | {% with request.POST|get_list:'tags' as tags_list%} 81 | 92 | {% endwith %} 93 |
94 |
95 |
96 |
97 |
98 |
99 | 100 | 103 |
104 |
105 | 106 | 109 |
110 |
111 | 112 | 115 |
116 |
117 |
118 |
119 |
120 |
121 | 122 | 125 |
126 |
127 | 128 | 131 |
132 |
133 |
134 |
135 |
136 | 137 | 138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | {% endblock %} 150 | {% block script %} 151 | 194 | {% endblock %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/blog_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'django_blog_it/admin/base.html' %} 2 | {% block content %} 3 | 4 |
5 |
6 |
7 |
8 |
9 |
10 | Blog List New Blog Post 11 |
12 |
13 | 26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% for blog in blogs_list %} 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {% endfor %} 55 | 56 |
TitleAuthorCategoryStatusCreatedPublishedActions
{{ blog.title }}{{ blog.created_by }}{{ blog.category|default_if_none:'--' }}{{ blog.status }}{{ blog.created_on_arrow }}{{ blog.published_on_arrow }}
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {% for blog in pages_list %} 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | {% endfor %} 88 | 89 |
TitleAuthorCategoryStatusCreatedPublishedActions
{{ blog.title }}{{ blog.created_by }}{{ blog.category|default_if_none:'--' }}{{ blog.status }}{{ blog.created_on_arrow }}{{ blog.published_on_arrow }}
90 |
91 |
92 |
93 |
94 |
95 |
96 | 119 |
120 |
121 |
122 |
123 | 124 | {% endblock %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/blog_new.html: -------------------------------------------------------------------------------- 1 | {%extends 'django_blog_it/admin/base.html' %} 2 | {% load role_tags%} 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 |
10 | Create Article View Blog List 11 |
12 |
13 |
14 |
15 |
16 | {% csrf_token %} 17 |
18 |
19 |
20 | 21 | 22 | {{form.title | striptags}} 23 |
24 |
25 |
26 |
27 |
28 |
29 | 30 | 31 | {{form.slug | striptags}} 32 |
33 |
34 |
35 |
36 | 37 | 43 |
44 |
45 |
46 | 47 |
48 | 49 | 50 |
51 | {{form.publish_on | striptags}} 52 | 53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 |
61 |
62 |
63 |
64 | 65 | 66 |
67 |
68 |
69 |
70 | 71 | 72 |
73 |
74 |
75 |
76 |
77 |
78 | 79 | {% with request.POST|get_list:'tags' as tags_list%} 80 | 90 | {% endwith %} 91 |
92 |
93 |
94 |
95 |
96 |    97 |
98 | 99 | 102 |
103 |
104 | 105 | 108 |
109 |
110 | 111 | 114 |
115 |
116 |
117 |
118 |
119 |    120 |
121 | 122 | 125 |
126 |
127 | 128 | 131 |
132 |
133 |
134 |
135 |
136 | 137 | 138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | 166 | {% endblock %} 167 | {% block script %} 168 | 187 | {% endblock %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/ckeditor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Django-Blog-It 19 | 20 | 21 | 22 | 23 | 24 | 25 | 70 | -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/dashboard.html: -------------------------------------------------------------------------------- 1 | {% extends 'django_blog_it/admin/base.html' %} 2 | {% load static %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/grape_js.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | GrapesJS Preset Webpage 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 34 |
35 |
36 | 37 | 38 | 44 | -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/preview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{data.title}} 5 | 6 | 7 | {{data.content|safe}} 8 | 9 | -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/admin/user_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'django_blog_it/admin/base.html' %} 2 | {% load role_tags %} 3 | {% block content %} 4 | 5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 15 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% for user in users %} 34 | 35 | 36 | {% with user.user_roles.all|get_role_list as roles %} 37 | 38 | 39 | 40 | {% endwith %} 41 | 42 | {% endfor %} 43 | 44 |
UserBlog AdminBlog PublisherBlog Auther
{{user.email}}
45 |
46 |
47 |
48 |
49 | 68 |
69 |
70 |
71 |
72 | 73 | {% endblock %} 74 | {% block script %} 75 | 99 | {% endblock %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/blog/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block meta_content %} 9 | {% endblock %} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Django-Blog-It 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 |
30 | 49 |
50 |
51 |
52 |
53 | {% block content %} 54 | {% endblock %} 55 | 56 | 57 | 58 | 59 | {% block script %} 60 | {% endblock %} 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/blog/blog.html: -------------------------------------------------------------------------------- 1 | {% extends 'django_blog_it/blog/base.html' %} 2 | {% block meta_content %} 3 | 4 | 5 | 6 | {% endblock %} 7 | {% block content %} 8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |

{{data.content | safe}}

17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {% endblock content %} -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/page/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block meta_content %} 9 | {% endblock %} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Django-Blog-It 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 |
30 | 40 |
41 |
42 |
43 |
44 | {% block content %} 45 | {% endblock %} 46 | 47 | 48 | 49 | 50 | {% block script %} 51 | {% endblock %} 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /django_blog_it/templates/django_blog_it/page/page.html: -------------------------------------------------------------------------------- 1 | {% extends 'django_blog_it/page/base.html' %} 2 | {% block meta_content %} 3 | 4 | 5 | 6 | {% endblock %} 7 | {% block content %} 8 |
9 |
10 |
11 |
12 |
13 |
14 |

{{data.title}}

15 |
16 |
17 |
18 |
19 |

{{data.content | safe}}

20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {% endblock content %} -------------------------------------------------------------------------------- /django_blog_it/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicroPyramid/django-blog-it/35596ce201b5cbf037e958708788581039a16346/django_blog_it/templatetags/__init__.py -------------------------------------------------------------------------------- /django_blog_it/templatetags/role_tags.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django_blog_it.models import BlogUser 3 | 4 | register = template.Library() 5 | 6 | 7 | @register.simple_tag 8 | def rolelength(user): 9 | role_length = BlogUser.objects.filter( 10 | user=user).values_list('role', flat=True).count() 11 | if role_length != 0: 12 | return True 13 | else: 14 | return False 15 | 16 | 17 | @register.simple_tag 18 | def adminuser(user): 19 | role = BlogUser.objects.filter( 20 | user=user, role='blog_admin').exists() 21 | return role 22 | 23 | 24 | @register.filter 25 | def get_list(dictionary, key): 26 | return dictionary.getlist(key) 27 | 28 | 29 | @register.filter 30 | def get_role_list(obj): 31 | return obj.values_list('role', flat=True) 32 | 33 | 34 | @register.filter 35 | def to_str(value): 36 | """converts int to string""" 37 | return str(value) 38 | -------------------------------------------------------------------------------- /django_blog_it/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /django_blog_it/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import (dashboard, user_list, user_role, 3 | blog_list, blog_new, blog_preview, 4 | blog_edit, blog_delete, blog_category, 5 | blog_category_list, blog_catergory_edit, 6 | blog_detail, page_detail, 7 | blog_category_delete, blog_content_edit_with_grapejs, 8 | get_blog_content, blog_content_edit_with_ckeditor) 9 | 10 | from django_blog_it.sitemaps import sitemap_xml 11 | from django.conf.urls import url 12 | 13 | app_name = "django_blog_it" 14 | 15 | urlpatterns = [ 16 | path('blog/admin/', dashboard, name='dashboard'), 17 | path('blog/admin/users/', user_list, name='user_list'), 18 | path('blog/admin/roles/', user_role, name='user_role'), 19 | path('blog/admin/articles/', blog_list, name='blog_list'), 20 | path('blog/admin/new-article/', blog_new, name='blog_new'), 21 | path('blog/admin/preview//', blog_preview, name='blog_preview'), 22 | path('blog/admin//edit/', blog_edit, name='blog_edit'), 23 | path('blog/admin//content-edit-grapejs/', blog_content_edit_with_grapejs, name='blog_content_edit_grapejs'), 24 | path('blog/admin//content-edit-ckeditor/', blog_content_edit_with_ckeditor, name='blog_content_edit_ckeditor'), 25 | path('blog/admin//preview/', get_blog_content, name='get_blog_content'), 26 | path('blog/admin//delete/', blog_delete, name='blog_delete'), 27 | path('blog/admin/catergory/', blog_category, name='blog_category'), 28 | path('blog/admin/catergory/list/', 29 | blog_category_list, name='blog_category_list'), 30 | path('blog/admin/catergory//edit/', 31 | blog_catergory_edit, name='blog_catergory_edit'), 32 | path('blog/admin/catergory//delete/', 33 | blog_category_delete, name='blog_category_delete'), 34 | path('blog//', blog_detail, name='blog_detail'), 35 | path('/', page_detail, name='page_detail'), 36 | url(r'^blog/sitemap.xml$', sitemap_xml, name="sitemap_xml"), 37 | ] 38 | -------------------------------------------------------------------------------- /django_blog_it/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, get_object_or_404, redirect 2 | from django_blog_it.models import (BlogUser, Category, Tag, Article) 3 | from django_blog_it.forms import CategoryForm, ArticleForm 4 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 5 | from django.contrib.auth.decorators import login_required 6 | from django.http.response import JsonResponse 7 | from django.http import Http404 8 | from django.urls import reverse 9 | from django.contrib.auth import get_user_model 10 | 11 | 12 | def required_roles(roles): 13 | def user_role(function): 14 | def wrap(request, *args, **kwargs): 15 | user = BlogUser.objects.filter( 16 | user=request.user, is_active=True).values_list( 17 | 'role', flat=True) 18 | for role in user: 19 | if (role in roles) or ( 20 | 'admin' in roles and request.user.is_superuser): 21 | return function(request, *args, **kwargs) 22 | raise Http404 23 | return wrap 24 | return user_role 25 | 26 | 27 | @login_required 28 | def dashboard(request): 29 | return render(request, 'django_blog_it/admin/dashboard.html') 30 | 31 | 32 | @login_required 33 | @required_roles(["admin", 'blog_admin']) 34 | def user_list(request): 35 | total_users = get_user_model().objects.all() 36 | page = request.GET.get('page', 1) 37 | if request.GET.get('name'): 38 | name = request.GET.get('name') 39 | total_users = total_users.filter(email__icontains=name) 40 | if request.GET.get('page_length'): 41 | length = request.GET.get('page_length') 42 | else: 43 | length = 15 44 | paginator = Paginator(total_users, length) 45 | try: 46 | users = paginator.page(page) 47 | except PageNotAnInteger: 48 | users = paginator.page(1) 49 | except EmptyPage: 50 | users = paginator.page(paginator.num_pages) 51 | return render(request, 'django_blog_it/admin/user_list.html', 52 | {'users': users, 'list_length': length}) 53 | 54 | 55 | @login_required 56 | @required_roles(["admin", 'blog_admin']) 57 | def user_role(request): 58 | if request.method == "POST": 59 | role = request.POST.get('role') 60 | user = request.POST.get('user_id') 61 | if role: 62 | if not BlogUser.objects.filter(user_id=user, role=role).exists(): 63 | BlogUser.objects.create(user_id=user, role=role) 64 | return JsonResponse({"response": 'done'}) 65 | else: 66 | user_id = request.GET.get('user_id') 67 | role = request.GET.get('role') 68 | blog = BlogUser.objects.filter(user_id=user_id, role=role).first() 69 | blog.delete() 70 | return JsonResponse({"response": 'done'}) 71 | 72 | 73 | @login_required 74 | @required_roles(["admin", "blog_admin", 'blog_publisher', 'blog_author']) 75 | def blog_list(request): 76 | article_data = Article.objects.order_by('id') 77 | if request.GET.get('name'): 78 | name = request.GET.get('name') 79 | article_data = article_data.filter( 80 | title__icontains=name) 81 | blogs_list = article_data.filter(is_page=False) 82 | pages_list = article_data.filter(is_page=True) 83 | return render( 84 | request, 'django_blog_it/admin/blog_list.html', {'blogs_list': blogs_list, 'pages_list': pages_list}) 85 | 86 | 87 | @login_required 88 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 89 | def blog_new(request): 90 | category_list = Category.objects.filter(created_by=request.user) 91 | tag_ids = Article.objects.values_list("tags", flat=True) 92 | tags = Tag.objects.filter(id__in=tag_ids) 93 | if request.method == 'POST': 94 | form = ArticleForm(request.POST, type=request.POST.get('is_page')) 95 | if form.is_valid(): 96 | blog = form.save(commit=False) 97 | blog.created_by = request.user 98 | if request.POST.get('category'): 99 | blog.category = Category.objects.filter( 100 | id=request.POST['category']).first() 101 | if request.POST.get('is_page') == "Page": 102 | blog.is_page = True 103 | else: 104 | blog.is_page = False 105 | blog.save() 106 | if request.POST.getlist('tags'): 107 | splitted_tags = request.POST.getlist("tags") 108 | for t in splitted_tags: 109 | tag = Tag.objects.filter(name=t.lower()) 110 | if tag: 111 | tag = tag[0] 112 | else: 113 | tag = Tag.objects.create(name=t.lower()) 114 | blog.tags.add(tag) 115 | return redirect('django_blog_it:blog_list') 116 | return render(request, 'django_blog_it/admin/blog_new.html', {'category': category_list, 'form': form.errors, 'tags': tags}) 117 | return render(request, 'django_blog_it/admin/blog_new.html', {'category': category_list, 'tags': tags}) 118 | 119 | 120 | @login_required 121 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 122 | def blog_edit(request, pk): 123 | blog_content = get_object_or_404(Article, id=pk) 124 | category_list = Category.objects.filter( 125 | created_by=request.user) 126 | tag_ids = Article.objects.filter( 127 | created_by=request.user).values_list("tags", flat=True) 128 | tags = Tag.objects.filter(id__in=tag_ids) 129 | if request.method == 'POST': 130 | form = ArticleForm(request.POST, instance=blog_content, 131 | type=request.POST.get('is_page')) 132 | if form.is_valid(): 133 | blog = form.save(commit=False) 134 | if request.POST.get("is_page") == "Page": 135 | blog.is_page = True 136 | else: 137 | blog.is_page = False 138 | blog.save() 139 | if request.POST.getlist('tags'): 140 | blog_content.tags.clear() 141 | tags = request.POST.getlist('tags') 142 | for t in tags: 143 | tag = Tag.objects.filter(name=t.lower()) 144 | if tag: 145 | tag = tag[0] 146 | else: 147 | tag = Tag.objects.create(name=t.lower()) 148 | blog.tags.add(tag) 149 | return redirect('django_blog_it:blog_list') 150 | return render(request, 'django_blog_it/admin/blog_edit.html', {'form': form.errors, 'blog': blog_content, 'category': category_list, 'tags': tags}) 151 | return render(request, 'django_blog_it/admin/blog_edit.html', {'blog': blog_content, 'category': category_list, 'tags': tags}) 152 | 153 | 154 | @login_required 155 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 156 | def blog_delete(request, pk): 157 | blog = get_object_or_404(Article, id=pk) 158 | blog.delete() 159 | return redirect('django_blog_it:blog_list') 160 | 161 | 162 | @login_required 163 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 164 | def blog_category_list(request): 165 | category_list = Category.objects.order_by('id') 166 | page = request.GET.get('page', 1) 167 | if request.GET.get('name'): 168 | name = request.GET.get('name') 169 | category_list = category_list.filter(name__icontains=name) 170 | if request.GET.get('page_length'): 171 | length = request.GET.get('page_length') 172 | else: 173 | length = 15 174 | paginator = Paginator(category_list, length) 175 | try: 176 | blog = paginator.page(page) 177 | except PageNotAnInteger: 178 | blog = paginator.page(1) 179 | except EmptyPage: 180 | blog = paginator.page(paginator.num_pages) 181 | return render(request, 'django_blog_it/admin/blog_category_list.html', {'blogdata': blog, 'list_length': length}) 182 | 183 | 184 | @login_required 185 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 186 | def blog_category(request): 187 | if request.method == 'POST': 188 | form = CategoryForm(request.POST) 189 | if form.is_valid(): 190 | blog = form.save(commit=False) 191 | blog.created_by = request.user 192 | blog.is_active = True 193 | blog.save() 194 | return redirect('django_blog_it:blog_category_list') 195 | return render(request, 'django_blog_it/admin/blog_category_create.html', {'form': form.errors 196 | }) 197 | return render(request, 'django_blog_it/admin/blog_category_create.html') 198 | 199 | 200 | @login_required 201 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 202 | def blog_catergory_edit(request, pk): 203 | blog = get_object_or_404(Category, id=pk) 204 | if request.method == 'POST': 205 | form = CategoryForm(request.POST, instance=blog) 206 | if form.is_valid(): 207 | form.save() 208 | return redirect('django_blog_it:blog_category_list') 209 | return render(request, 'django_blog_it/admin/blog_category_edit.html', 210 | {'form': form.errors, 'blog_data': blog}) 211 | return render(request, 'django_blog_it/admin/blog_category_edit.html', {'blog_data': blog}) 212 | 213 | 214 | @login_required 215 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 216 | def blog_category_delete(request, pk): 217 | blog = get_object_or_404(Category, id=pk) 218 | blog.delete() 219 | return redirect('django_blog_it:blog_category_list') 220 | 221 | 222 | @login_required 223 | @required_roles(["admin", 'blog_admin', 'blog_publisher', 'blog_author']) 224 | def page_category(request): 225 | if request.method == 'POST': 226 | form = CategoryForm(request.POST) 227 | if form.is_valid(): 228 | blog = form.save(commit=False) 229 | blog.created_by = request.user 230 | blog.is_active = True 231 | blog.is_page = True 232 | blog.save() 233 | return redirect('django_blog_it:page_category_list') 234 | return render(request, 'django_blog_it/admin/page_category.html', {'form': form.errors 235 | }) 236 | return render(request, 'django_blog_it/admin/page_category.html') 237 | 238 | 239 | @login_required 240 | def blog_detail(request, slug): 241 | blog = get_object_or_404(Article, slug=slug, is_page=False) 242 | return render(request, 'django_blog_it/blog/blog.html', {'data': blog}) 243 | 244 | 245 | @login_required 246 | def blog_preview(request, pk): 247 | blog = get_object_or_404(Article, id=pk) 248 | return render(request, 'django_blog_it/admin/preview.html', {'data': blog}) 249 | 250 | 251 | @login_required 252 | def page_detail(request, slug): 253 | blog = get_object_or_404(Article, slug=slug, is_page=True) 254 | return render(request, 'django_blog_it/page/page.html', {'data': blog}) 255 | 256 | 257 | @login_required 258 | def blog_content_edit_with_grapejs(request, pk): 259 | blog = get_object_or_404(Article, pk=pk) 260 | if request.method == "GET": 261 | landinpage_url = reverse( 262 | "django_blog_it:get_blog_content", kwargs={"pk": pk} 263 | ) 264 | return render( 265 | request, 266 | "django_blog_it/admin/grape_js.html", 267 | {"landinpage_url": landinpage_url}, 268 | ) 269 | if request.method == "POST": 270 | html = request.POST.get("html") 271 | css = request.POST.get("css") 272 | if html and css: 273 | html_css = html + "" 274 | blog.content = html_css 275 | blog.save() 276 | url = reverse("django_blog_it:blog_edit", kwargs={"pk": pk}) 277 | return JsonResponse({"redirect_url": url}) 278 | 279 | 280 | @login_required 281 | def get_blog_content(request, pk): 282 | blog = get_object_or_404(Article, pk=pk) 283 | if request.method == "POST": 284 | html = blog.content 285 | return JsonResponse({"html": html}) 286 | return render(request, 'django_blog_it/admin/preview.html', {"data": blog}) 287 | 288 | 289 | @login_required 290 | def blog_content_edit_with_ckeditor(request, pk): 291 | blog = get_object_or_404(Article, pk=pk) 292 | if request.method == "GET": 293 | return render( 294 | request, 295 | "django_blog_it/admin/ckeditor.html", {"blog": blog} 296 | ) 297 | if request.method == "POST": 298 | content = request.POST.get("content") 299 | if content: 300 | blog.content = content 301 | blog.save() 302 | url = reverse("django_blog_it:blog_edit", kwargs={"pk": pk}) 303 | return JsonResponse({"redirect_url": url}) 304 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MicroSite.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MicroSite.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/MicroSite" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/MicroSite" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicroPyramid/django-blog-it/35596ce201b5cbf037e958708788581039a16346/docs/__init__.py -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # django-blog-it documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Dec 29 20:41:44 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.insert(0, os.path.abspath('.')) 20 | 21 | # -- General configuration ------------------------------------------------ 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be 27 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 28 | # ones. 29 | extensions = [ 30 | 'sphinx.ext.autodoc', 31 | ] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['ntemplates'] 35 | 36 | # The suffix of source filenames. 37 | source_suffix = '.rst' 38 | 39 | # The encoding of source files. 40 | #source_encoding = 'utf-8-sig' 41 | 42 | # The master toctree document. 43 | master_doc = 'index' 44 | 45 | # General information about the project. 46 | project = u'django-blog-it' 47 | copyright = u'2016, MicroPyramid' 48 | 49 | # The version info for the project you're documenting, acts as replacement for 50 | # |version| and |release|, also used in various other places throughout the 51 | # built documents. 52 | # 53 | # The short X.Y version. 54 | version = '0.3.1' 55 | # The full version, including alpha/beta/rc tags. 56 | release = '0.3.1' 57 | 58 | # The language for content autogenerated by Sphinx. Refer to documentation 59 | # for a list of supported languages. 60 | #language = None 61 | 62 | # There are two options for replacing |today|: either, you set today to some 63 | # non-false value, then it is used: 64 | #today = '' 65 | # Else, today_fmt is used as the format for a strftime call. 66 | #today_fmt = '%B %d, %Y' 67 | 68 | # List of patterns, relative to source directory, that match files and 69 | # directories to ignore when looking for source files. 70 | exclude_patterns = [] 71 | 72 | # The reST default role (used for this markup: `text`) to use for all 73 | # documents. 74 | #default_role = None 75 | 76 | # If true, '()' will be appended to :func: etc. cross-reference text. 77 | #add_function_parentheses = True 78 | 79 | # If true, the current module name will be prepended to all description 80 | # unit titles (such as .. function::). 81 | #add_module_names = True 82 | 83 | # If true, sectionauthor and moduleauthor directives will be shown in the 84 | # output. They are ignored by default. 85 | #show_authors = False 86 | 87 | # The name of the Pygments (syntax highlighting) style to use. 88 | pygments_style = 'sphinx' 89 | 90 | # A list of ignored prefixes for module index sorting. 91 | #modindex_common_prefix = [] 92 | 93 | # If true, keep warnings as "system message" paragraphs in the built documents. 94 | #keep_warnings = False 95 | 96 | 97 | # -- Options for HTML output ---------------------------------------------- 98 | 99 | # The theme to use for HTML and HTML Help pages. See the documentation for 100 | # a list of builtin themes. 101 | html_theme = 'default' 102 | 103 | # Theme options are theme-specific and customize the look and feel of a theme 104 | # further. For a list of options available for each theme, see the 105 | # documentation. 106 | #html_theme_options = {} 107 | 108 | # Add any paths that contain custom themes here, relative to this directory. 109 | #html_theme_path = [] 110 | 111 | # The name for this set of Sphinx documents. If None, it defaults to 112 | # " v documentation". 113 | #html_title = None 114 | 115 | # A shorter title for the navigation bar. Default is the same as html_title. 116 | #html_short_title = None 117 | 118 | # The name of an image file (relative to this directory) to place at the top 119 | # of the sidebar. 120 | #html_logo = None 121 | 122 | # The name of an image file (within the static path) to use as favicon of the 123 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 124 | # pixels large. 125 | #html_favicon = None 126 | 127 | # Add any paths that contain custom static files (such as style sheets) here, 128 | # relative to this directory. They are copied after the builtin static files, 129 | # so a file named "default.css" will overwrite the builtin "default.css". 130 | html_static_path = ['nstatic'] 131 | 132 | # Add any extra paths that contain custom files (such as robots.txt or 133 | # .htaccess) here, relative to this directory. These files are copied 134 | # directly to the root of the documentation. 135 | #html_extra_path = [] 136 | 137 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 138 | # using the given strftime format. 139 | #html_last_updated_fmt = '%b %d, %Y' 140 | 141 | # If true, SmartyPants will be used to convert quotes and dashes to 142 | # typographically correct entities. 143 | #html_use_smartypants = True 144 | 145 | # Custom sidebar templates, maps document names to template names. 146 | #html_sidebars = {} 147 | 148 | # Additional templates that should be rendered to pages, maps page names to 149 | # template names. 150 | #html_additional_pages = {} 151 | 152 | # If false, no module index is generated. 153 | #html_domain_indices = True 154 | 155 | # If false, no index is generated. 156 | #html_use_index = True 157 | 158 | # If true, the index is split into individual pages for each letter. 159 | #html_split_index = False 160 | 161 | # If true, links to the reST sources are added to the pages. 162 | #html_show_sourcelink = True 163 | 164 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 165 | #html_show_sphinx = True 166 | 167 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 168 | #html_show_copyright = True 169 | 170 | # If true, an OpenSearch description file will be output, and all pages will 171 | # contain a tag referring to it. The value of this option must be the 172 | # base URL from which the finished HTML is served. 173 | #html_use_opensearch = '' 174 | 175 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 176 | #html_file_suffix = None 177 | 178 | # Output file base name for HTML help builder. 179 | htmlhelp_basename = 'djangoblogitdoc' 180 | 181 | 182 | # -- Options for LaTeX output --------------------------------------------- 183 | 184 | latex_elements = { 185 | # The paper size ('letterpaper' or 'a4paper'). 186 | #'papersize': 'letterpaper', 187 | 188 | # The font size ('10pt', '11pt' or '12pt'). 189 | #'pointsize': '10pt', 190 | 191 | # Additional stuff for the LaTeX preamble. 192 | #'preamble': '', 193 | } 194 | 195 | # Grouping the document tree into LaTeX files. List of tuples 196 | # (source start file, target name, title, 197 | # author, documentclass [howto, manual, or own class]). 198 | latex_documents = [ 199 | ('index', 'django-blog-it.tex', u'django-blog-it Documentation', 200 | u'django-blog-it', 'manual'), 201 | ] 202 | 203 | # The name of an image file (relative to this directory) to place at the top of 204 | # the title page. 205 | #latex_logo = None 206 | 207 | # For "manual" documents, if this is true, then toplevel headings are parts, 208 | # not chapters. 209 | #latex_use_parts = False 210 | 211 | # If true, show page references after internal links. 212 | #latex_show_pagerefs = False 213 | 214 | # If true, show URL addresses after external links. 215 | #latex_show_urls = False 216 | 217 | # Documents to append as an appendix to all manuals. 218 | #latex_appendices = [] 219 | 220 | # If false, no module index is generated. 221 | #latex_domain_indices = True 222 | 223 | 224 | # -- Options for manual page output --------------------------------------- 225 | 226 | # One entry per manual page. List of tuples 227 | # (source start file, name, description, authors, manual section). 228 | man_pages = [ 229 | ('index', 'django-blog-it', u'django-blog-it Documentation', 230 | [u'django-blog-it'], 1) 231 | ] 232 | 233 | # If true, show URL addresses after external links. 234 | #man_show_urls = False 235 | 236 | 237 | # -- Options for Texinfo output ------------------------------------------- 238 | 239 | # Grouping the document tree into Texinfo files. List of tuples 240 | # (source start file, target name, title, author, 241 | # dir menu entry, description, category) 242 | texinfo_documents = [ 243 | ('index', 'django-blog-it', u'django-blog-it Documentation', 244 | u'django-blog-it', 'django-blog-it', 'One line description of project.', 245 | 'Miscellaneous'), 246 | ] 247 | 248 | # Documents to append as an appendix to all manuals. 249 | #texinfo_appendices = [] 250 | 251 | # If false, no module index is generated. 252 | #texinfo_domain_indices = True 253 | 254 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 255 | #texinfo_show_urls = 'footnote' 256 | 257 | # If true, do not generate a @detailmenu in the "Top" node's menu. 258 | #texinfo_no_detailmenu = False 259 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | django-blog-it's documentation v0.1: 2 | ===================================== 3 | 4 | Introduction: 5 | ============= 6 | 7 | django-blog-it is simple and completely customizable blog application. You can integrate it into your project and customise the blog application or just host it to post your articles. 8 | 9 | Source Code is available in Micropyramid Repository(https://github.com/MicroPyramid/django-blog-it.git). 10 | 11 | Modules used: 12 | * Pillow 13 | * Django Simple Pagination 14 | 15 | 16 | Requirements 17 | ====================== 18 | 19 | ====== ==================== 20 | Python >= 2.6 (or Python 3.4) 21 | Django = 1.9 22 | jQuery >= 1.7 23 | ====== ==================== 24 | 25 | Installation Procedure 26 | ====================== 27 | 28 | 1. Install django-blog-it using the following command:: 29 | 30 | pip install django-blog-it 31 | 32 | (or) 33 | 34 | git clone git://github.com/micropyramid/django-blog-it.git 35 | 36 | cd django_blog_it 37 | 38 | python setup.py install 39 | 40 | 41 | 2. After installing/cloning this, add the following settings in the virtual env/bin/activate file to start discussions on blog articles :: 42 | 43 | You can create/get your disqus account at https://disqus.com/profile/login/ 44 | 45 | # Disquss details 46 | 47 | DISQUSSHORTNAME="Your Disquss Short Name" 48 | 49 | export DISQUSSHORTNAME 50 | 51 | 3. Use virtualenv to install requirements:: 52 | 53 | pip install -r requirements.txt 54 | 55 | 56 | Working modules 57 | =============== 58 | * Create Blog Posts. 59 | * A Complete Blog with articles, categories, tags, archievs. 60 | * Blog Post History. 61 | * Blog Post Trash Management. 62 | 63 | 64 | Planned Modules 65 | =============== 66 | * Blog pages 67 | * Contact us page (configurable) 68 | * google analytics 69 | * SEO compliant 70 | * Configurable contact us page. 71 | * Dynamic Menu. 72 | * Social Login. 73 | 74 | We are always looking to help you customize the whole or part of the code as you like. 75 | 76 | 77 | -------------------------------------------------------------------------------- /loaders.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.conf import settings 3 | from django.template.loaders.base import Loader as BaseLoader 4 | 5 | from django.template import TemplateDoesNotExist 6 | from django_blog_it.models import Theme 7 | 8 | 9 | class Loader(BaseLoader): 10 | is_usable = True 11 | 12 | def load_template_source(self, template_name, template_dirs=None): 13 | 14 | themes = Theme.objects.filter(enabled=True) 15 | for theme in themes: 16 | filepath = os.path.join(os.path.dirname(__file__), 'themes', theme.name, 'templates', template_name) 17 | try: 18 | file = open(filepath) 19 | try: 20 | return (file.read().decode(settings.FILE_CHARSET), filepath) 21 | finally: 22 | file.close() 23 | except IOError: 24 | pass 25 | 26 | raise TemplateDoesNotExist("Could not find template '%s'." % template_name) 27 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -r sandbox/requirements.txt 2 | 3 | # Babel==2.2.0 4 | # Django==1.9.4 5 | # Jinja2==2.8 6 | # MakupSafe==0.23 7 | # Pillow==3.1.1 8 | # Pygments==2.1.3 9 | # Sphinx==1.4 10 | # alabaster==0.7.7 11 | # argparse==1.4.0 12 | # boto==2.39.0 13 | # coverage==4.0.3 14 | # django-simple-pagination==1.1.4 15 | # django-storages==1.4 16 | # docutils==0.12 17 | # imagesize==0.7.0 18 | # pytz==2016.3 19 | # requests==2.9.1 20 | # six==1.10.0 21 | # snowballstemmer==1.2.1 22 | # sphinx-rtd-theme==0.1.9 23 | # wheel==0.29.0 -------------------------------------------------------------------------------- /sandbox/blog_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicroPyramid/django-blog-it/35596ce201b5cbf037e958708788581039a16346/sandbox/blog_test/__init__.py -------------------------------------------------------------------------------- /sandbox/blog_test/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for blog_test project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.9.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.9/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.9/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'g@dy!4vpy40o**opk5qc*nk(-w7m-(ra90()tcwa((#t-x5_1n' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'django_blog_it.django_blog_it', 41 | 'simple_pagination', 42 | ] 43 | 44 | MIDDLEWARE_CLASSES = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 51 | 'django.contrib.messages.middleware.MessageMiddleware', 52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 53 | ] 54 | 55 | ROOT_URLCONF = 'blog_test.urls' 56 | 57 | TEMPLATES = [ 58 | { 59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 | 'DIRS': [], 61 | 'APP_DIRS': True, 62 | 'OPTIONS': { 63 | 'context_processors': [ 64 | 'django.template.context_processors.debug', 65 | 'django.template.context_processors.request', 66 | 'django.contrib.auth.context_processors.auth', 67 | 'django.contrib.messages.context_processors.messages', 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = 'blog_test.wsgi.application' 74 | 75 | 76 | # Database 77 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases 78 | import dj_database_url 79 | 80 | DATABASES = { 81 | 'default': dj_database_url.config( 82 | default='sqlite:////{0}'.format(os.path.join(BASE_DIR, 'db.sqlite3')) 83 | ) 84 | } 85 | 86 | # Password validation 87 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators 88 | 89 | AUTH_PASSWORD_VALIDATORS = [ 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 92 | }, 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 101 | }, 102 | ] 103 | 104 | 105 | # Internationalization 106 | # https://docs.djangoproject.com/en/1.9/topics/i18n/ 107 | 108 | LANGUAGE_CODE = 'en-us' 109 | 110 | TIME_ZONE = 'UTC' 111 | 112 | USE_I18N = True 113 | 114 | USE_L10N = True 115 | 116 | USE_TZ = True 117 | 118 | 119 | # Static files (CSS, JavaScript, Images) 120 | # https://docs.djangoproject.com/en/1.9/howto/static-files/ 121 | 122 | STATICFILES_FINDERS = ( 123 | 'django.contrib.staticfiles.finders.FileSystemFinder', 124 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 125 | ) 126 | 127 | STATIC_URL = '/static/' 128 | 129 | # STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' 130 | 131 | # STATIC_ROOT = (os.path.join(BASE_DIR, "static")) 132 | STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] 133 | -------------------------------------------------------------------------------- /sandbox/blog_test/urls.py: -------------------------------------------------------------------------------- 1 | """blog_test URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.9/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url, include 17 | from django.contrib import admin 18 | # import django_blog_it 19 | 20 | urlpatterns = [ 21 | url(r'^admin/', admin.site.urls), 22 | url(r'', include('django_blog_it.urls')), 23 | ] 24 | -------------------------------------------------------------------------------- /sandbox/blog_test/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for blog_test 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.9/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | from whitenoise.django import DjangoWhiteNoise 14 | 15 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "blog_test.settings") 16 | 17 | application = get_wsgi_application() 18 | application = DjangoWhiteNoise(application) 19 | -------------------------------------------------------------------------------- /sandbox/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", "blog_test.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /sandbox/requirements.txt: -------------------------------------------------------------------------------- 1 | boto==2.49.0 2 | Django==2.2.10 3 | django-simple-pagination==1.1.4 4 | django-storages==1.9.1 5 | Pillow==7.0.0 6 | requests==2.23.0 7 | gunicorn==19.6.0 8 | whitenoise==3.1 9 | dj-database-url==0.4.1 10 | psycopg2==2.7.1 11 | microurl==0.1.1 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: 5 | README = readme.read() 6 | 7 | # allow setup.py to be run from any path 8 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 9 | PROJECT_NAME = 'django_blog_it' 10 | 11 | data_files = [] 12 | for dirpath, dirnames, filenames in os.walk(PROJECT_NAME): 13 | for i, dirname in enumerate(dirnames): 14 | if dirname.startswith('.'): 15 | del dirnames[i] 16 | if '__init__.py' in filenames: 17 | continue 18 | elif filenames: 19 | for f in filenames: 20 | data_files.append(os.path.join( 21 | dirpath[len(PROJECT_NAME) + 1:], f)) 22 | 23 | setup( 24 | name='django-blog-it', 25 | version='1.0', 26 | packages=find_packages(exclude=['tests', 'tests.*']), 27 | include_package_data=True, 28 | description='A simple installable app to mange web pages and blog posts', 29 | long_description=README, 30 | url='https://github.com/MicroPyramid/django-blog-it.git', 31 | author='Micropyramid', 32 | author_email='hello@micropyramid.com', 33 | classifiers=[ 34 | 'Environment :: Web Environment', 35 | 'Framework :: Django', 36 | 'Intended Audience :: Developers', 37 | 'Operating System :: OS Independent', 38 | 'License :: OSI Approved :: MIT License', 39 | 'Programming Language :: Python', 40 | 'Programming Language :: Python :: 2.7', 41 | 'Programming Language :: Python :: 3', 42 | 'Programming Language :: Python :: 3.2', 43 | 'Programming Language :: Python :: 3.3', 44 | 'Topic :: Internet :: WWW/HTTP', 45 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 46 | ], 47 | install_requires=[ 48 | 'Django', 49 | 'arrow' 50 | ], 51 | ) 52 | -------------------------------------------------------------------------------- /test_runner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | if __name__ == "__main__": 5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_blog_it.test_settings") 6 | 7 | from django.core.management import execute_from_command_line 8 | 9 | execute_from_command_line(sys.argv) 10 | --------------------------------------------------------------------------------