├── .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 |
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 |
11 |
12 |
13 |
14 |
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 |
12 |
13 |
14 |
15 |
25 |
26 |
27 |
28 | Title |
29 | Author |
30 | Meta Description |
31 | Meta Keywords |
32 | Actions |
33 |
34 |
35 |
36 | {% for blog in blogdata %}
37 |
38 | {{ blog.name }} |
39 | {{ blog.created_by.username }} |
40 | {{ blog.meta_description }} |
41 | {{ blog.meta_keywords }} |
42 | |
43 |
44 | {% endfor %}
45 |
46 |
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 |
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 |
12 |
13 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Title |
34 | Author |
35 | Category |
36 | Status |
37 | Created |
38 | Published |
39 | Actions |
40 |
41 |
42 |
43 | {% for blog in blogs_list %}
44 |
45 | {{ blog.title }} |
46 | {{ blog.created_by }} |
47 | {{ blog.category|default_if_none:'--' }} |
48 | {{ blog.status }} |
49 | {{ blog.created_on_arrow }} |
50 | {{ blog.published_on_arrow }} |
51 |
52 | |
53 |
54 | {% endfor %}
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Title |
67 | Author |
68 | Category |
69 | Status |
70 | Created |
71 | Published |
72 | Actions |
73 |
74 |
75 |
76 | {% for blog in pages_list %}
77 |
78 | {{ blog.title }} |
79 | {{ blog.created_by }} |
80 | {{ blog.category|default_if_none:'--' }} |
81 | {{ blog.status }} |
82 | {{ blog.created_on_arrow }} |
83 | {{ blog.published_on_arrow }} |
84 |
85 | |
86 |
87 | {% endfor %}
88 |
89 |
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 |
12 |
13 |
14 |
15 |
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 |
23 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------