diff --git a/blog/__init__.py b/blog/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/blog/admin.py b/blog/admin.py new file mode 100644 index 0000000..35bc892 --- /dev/null +++ b/blog/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin +from .models import Post + +admin.site.register(Post) + +# Register your models here. diff --git a/blog/apps.py b/blog/apps.py new file mode 100644 index 0000000..40ce2ec --- /dev/null +++ b/blog/apps.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class BlogConfig(AppConfig): + name = 'blog' diff --git a/blog/forms.py b/blog/forms.py new file mode 100644 index 0000000..805cb8a --- /dev/null +++ b/blog/forms.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from django import forms +from .models import Post +from taggit.forms import TagWidget + +class PostForm(forms.ModelForm): + + class Meta: + model = Post + fields = ('title', 'text', 'tags',) diff --git a/blog/migrations/0001_initial.py b/blog/migrations/0001_initial.py new file mode 100644 index 0000000..5bb6c3a --- /dev/null +++ b/blog/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-06-29 12:36 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Post', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('text', models.TextField()), + ('created_date', models.DateTimeField(default=django.utils.timezone.now)), + ('published_date', models.DateTimeField(blank=True, null=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/blog/migrations/0002_auto_20160815_1508.py b/blog/migrations/0002_auto_20160815_1508.py new file mode 100644 index 0000000..19d694b --- /dev/null +++ b/blog/migrations/0002_auto_20160815_1508.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-08-15 12:08 +from __future__ import unicode_literals + +from django.db import migrations +import tinymce.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='post', + name='text', + field=tinymce.models.HTMLField(), + ), + ] diff --git a/blog/migrations/0003_post_tags.py b/blog/migrations/0003_post_tags.py new file mode 100644 index 0000000..be46d09 --- /dev/null +++ b/blog/migrations/0003_post_tags.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-08-19 13:44 +from __future__ import unicode_literals + +from django.db import migrations +import taggit.managers + + +class Migration(migrations.Migration): + + dependencies = [ + ('taggit', '0002_auto_20150616_2121'), + ('blog', '0002_auto_20160815_1508'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='tags', + field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + ] diff --git a/blog/migrations/0004_auto_20161002_1251.py b/blog/migrations/0004_auto_20161002_1251.py new file mode 100644 index 0000000..c649ccb --- /dev/null +++ b/blog/migrations/0004_auto_20161002_1251.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-10-02 09:51 +from __future__ import unicode_literals + +from django.db import migrations +import taggit.managers + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0003_post_tags'), + ] + + operations = [ + migrations.AlterField( + model_name='post', + name='tags', + field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + ] diff --git a/blog/migrations/__init__.py b/blog/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/blog/models.py b/blog/models.py new file mode 100644 index 0000000..ba56b64 --- /dev/null +++ b/blog/models.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from django.db import models +from django.utils import timezone +from tinymce.models import HTMLField +from taggit.managers import TaggableManager + +class Post(models.Model): + author = models.ForeignKey('auth.User') + title = models.CharField(max_length=200) + text = HTMLField() + created_date = models.DateTimeField( + default=timezone.now) + published_date = models.DateTimeField( + blank=True, null=True) + tags = TaggableManager(blank=True) + + def publish(self): + self.published_date = timezone.now() + self.save() + + def __unicode__(self): + return self.title + diff --git a/blog/static/css/blog.css b/blog/static/css/blog.css new file mode 100644 index 0000000..ec764a6 --- /dev/null +++ b/blog/static/css/blog.css @@ -0,0 +1,138 @@ +.col {padding: 2em} + +button[name="action"] {display: block; float: right; margin: 1px} +a {color: #2980b9;} + +.block, .page-header { + display: inline-block; + width: 90%; + min-height: 100px; + margin: 5px; border-radius: 5px; box-shadow: 1px 1px 3px #999999; padding: 10px; padding-left: 8%; +} + +.close { +background: #606061; +color: #FFFFFF; +line-height: 25px; +position: absolute; +right: 12px; +text-align: center; +top: -10px; +} + +.menu_top {list-style: none; text-decoration: none;} +.menu_top li {display: inline; margin-right: 10px;} +.paginator {font-size: 25px; text-align: center; } +.paginator a {display: inline-block; padding: 2 1; text-decoration:none; text-align: center; } + +.page-header {height: 120px;} + +textarea{ + width: 90%; + margin:5px 10px; +} + +.right { + float: right; + text-align: end; +} + +.menu { + margin: 0; + padding: 0; + position: absolute; + display: table; + list-style: none; + display: table; + z-index: 2; +} + +.menu ul {display: none;} + +.menu li:hover ul { + display: block; + background: white; + box-shadow: 1px 1px 3px #999999; +} + +.menu li { + display: table-cell; + text-align: center; + direction: ltr; +} +.menu ul li { + display: block; + text-align: left; +} +.menu a { + display: block; + padding: 8px 14px; +} + +.menu, .sub-menu {padding: 0; margin: 0; white-space: nowrap;} + +.menu li:hover > a { + background: rgba(41, 128, 185, 0.7); + color: #fff; +} + +.dialog { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0,0,0,0.8); + z-index: 99999; + -webkit-transition: opacity 400ms ease-in; + -moz-transition: opacity 400ms ease-in; + transition: opacity 400ms ease-in; + display: none; + pointer-events: none; +} + +.dialog:target { +display: block; +pointer-events: auto; +} + +.dialog1 { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0,0,0,0.8); + z-index: 99999; + -webkit-transition: opacity 400ms ease-in; + -moz-transition: opacity 400ms ease-in; + transition: opacity 400ms ease-in; + display: none; + pointer-events: none; +} + +.dialog1:target { +display: block; +pointer-events: auto; +} + +.dialog > div { +width: 17em; +position: relative; +margin: 10% auto; +padding: 5px 20px 13px 20px; +border-radius: 10px; +background: #fff; +} + +table { +border: none; +border-collapse: collapse;} + +td {padding: 2 10;} + +tr:hover td { +background-color: rgba(41, 128, 185, 0.1); +} + + diff --git a/blog/static/css/css b/blog/static/css/css new file mode 100644 index 0000000..cb7c00f --- /dev/null +++ b/blog/static/css/css @@ -0,0 +1,3 @@ + h1 a { + color: #FCA205; + } diff --git a/blog/templates/blog/base.html b/blog/templates/blog/base.html new file mode 100644 index 0000000..c4f62af --- /dev/null +++ b/blog/templates/blog/base.html @@ -0,0 +1,58 @@ +{% load staticfiles %} + + + +BLOG + + + + + + + + + + + {% block content %} + {% endblock %} + + + + diff --git a/blog/templates/blog/post_detail.html b/blog/templates/blog/post_detail.html new file mode 100644 index 0000000..356a146 --- /dev/null +++ b/blog/templates/blog/post_detail.html @@ -0,0 +1,33 @@ +{% extends 'blog/base.html' %} + +{% block content %} +
+

{{ post.title }}

+

Теги: + {% if tags %} + {% for tag in tags %} + {{ tag }} + {% endfor %} + {% endif %} +

+ +

{{ post.text|safe|linebreaksbr }}

+ + {% if post.published_date %} + {{ post.published_date }} + {% endif %} + + +edit post +delete +
+
+

+
{% csrf_token %} + confirm deletion of {{ post.pk }} {{ post.title }} + +
+
+
+
+{% endblock %} diff --git a/blog/templates/blog/post_edit.html b/blog/templates/blog/post_edit.html new file mode 100644 index 0000000..b1f8264 --- /dev/null +++ b/blog/templates/blog/post_edit.html @@ -0,0 +1,11 @@ + {% extends 'blog/base.html' %} + + {% block content %} +
+
{% csrf_token %} + {{ form.as_p }} + +
+
+ {% endblock %} + diff --git a/blog/templates/blog/post_list.html b/blog/templates/blog/post_list.html new file mode 100644 index 0000000..46bfcda --- /dev/null +++ b/blog/templates/blog/post_list.html @@ -0,0 +1,22 @@ +{% extends 'blog/base.html' %} +{% load pagination_tags %} +{% block content %} + + {% for post in posts %} +
+

{{ post.title }}

+ {{ post.text|safe|linebreaksbr|truncatewords:80 }}
+

+{% if post.tags.names %} + Tags: + {% for tag in post.tags.names %} + {{ tag }} + {% endfor %} +{% endif %} +

+

{{ post.published_date}}

+
+ +{% endfor %} + +{% endblock content %} diff --git a/blog/templates/blog/tag_listing.html b/blog/templates/blog/tag_listing.html new file mode 100644 index 0000000..9d9cc41 --- /dev/null +++ b/blog/templates/blog/tag_listing.html @@ -0,0 +1,24 @@ +{% extends 'blog/base.html' %} +{% load pagination_tags %} +{% block content %} + +{% for post in tagged_posts %} +
+

{{ post.title }}

+ + {{ post.text|safe|linebreaksbr|truncatewords:80 }} + +
+

Tags: + {% for tag in post.tags.names %} + {{ tag }} + {% endfor %} +

+ +

+ {{ post.published_date}} +

+
+{% endfor %} + +{% endblock content %} diff --git a/blog/templates/main.html b/blog/templates/main.html new file mode 100644 index 0000000..58727f0 --- /dev/null +++ b/blog/templates/main.html @@ -0,0 +1,6 @@ +{% extends 'blog/base.html' %} + + +Тут будет что-нибудь написано =) +{% block content %} +{% endblock %} diff --git a/blog/tests.py b/blog/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/blog/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/blog/urls.py b/blog/urls.py new file mode 100644 index 0000000..988446d --- /dev/null +++ b/blog/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url +from . import views + +urlpatterns = [ + url(r'^(?P[0-9]+)/$', views.post_list, name='post_list'), + url(r'^post/(?P[0-9]+)/$', views.post_detail, name='post_detail'), + url(r'^new/$', views.post_new, name='post_new'), + url(r'^post/(?P[0-9]+)/edit/$', views.post_edit, name='post_edit'), + url(r'^tag/(?P[a-zA-Z]+)/$', views.search_by_tag, name='search_by_tag'), +] + diff --git a/blog/views.py b/blog/views.py new file mode 100644 index 0000000..2002cf3 --- /dev/null +++ b/blog/views.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +from django.shortcuts import render +from .models import Post +from django.utils import timezone +from django.shortcuts import render, get_object_or_404, render_to_response +from django.shortcuts import redirect +from .forms import PostForm +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.contrib.auth.decorators import login_required + +def post_list(request, page): + post_list = Post.objects.filter(published_date__lte=timezone.now()).order_by('-published_date')#[int(page)*5-5:int(page)*5] + paginator = Paginator(post_list, 5) + page = request.GET.get('page') + try: + posts = paginator.page(page) + + except PageNotAnInteger: + # If page is not an integer, deliver first page. + posts = paginator.page(1) + + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + posts = paginator.page(paginator.num_pages) + return render_to_response('blog/post_list.html', {'posts': posts}) + +def search_by_tag(request, tag): + tagged_posts = Post.objects.filter(tags__name=tag).order_by('-published_date') + + return render(request, 'blog/tag_listing.html', {'tagged_posts': tagged_posts}) + +def post_detail(request, pk): + post = get_object_or_404(Post, pk=pk) + tags = post.tags.names() + if request.POST.get('action') == 'delete': + post.delete() + print '------------delete---------------' + pk + return redirect('blog.views.post_list', 1) + + return render(request, 'blog/post_detail.html', {'post': post, 'tags': tags}) + +@login_required(login_url="/admin/login/?next=/blog/") +def post_new(request): + if request.method == "POST": + form = PostForm(request.POST) + if form.is_valid(): + post = form.save(commit=False) + post.author = request.user + post.published_date = timezone.now() + post.save() + form.save_m2m() + return redirect('blog.views.post_detail', pk=post.pk) + else: + form = PostForm() + return render(request, 'blog/post_edit.html', {'form': form}) + +@login_required(login_url="/admin/login/?next=/blog/") +def post_edit(request, pk): + post = get_object_or_404(Post, pk=pk) + if request.method == "POST": + form = PostForm(request.POST, instance = post) + if form.is_valid(): + post = form.save(commit=False) + post.author = request.user + #post.published_date = timezone.now() + post.save() + form.save_m2m() + return redirect('blog.views.post_detail', pk=post.pk) + else: + form = PostForm(instance=post) + return render(request, 'blog/post_edit.html', {'form': form}) diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..8a50ec0 --- /dev/null +++ b/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/mysite/__init__.py b/mysite/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mysite/settings.py b/mysite/settings.py new file mode 100644 index 0000000..2d00a5b --- /dev/null +++ b/mysite/settings.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +""" +Django settings for mysite project. + +Generated by 'django-admin startproject' using Django 1.9.7. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.9/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '1a+3jmf#g!q_gj66$^v9_&8uoev=%k49=j(%lp%!kvv$6x3bn^' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'blog', +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'mysite.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'mysite.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.9/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, '../db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.9/topics/i18n/ + +LANGUAGE_CODE = 'ru-RU' +USE_I18N = True + +TIME_ZONE = 'Europe/Moscow' + + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.9/howto/static-files/ + +PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static') + +ADMIN_TOOLS_MENU = 'myproject.menu.CustomMenu' +ADMIN_TOOLS_INDEX_DASHBOARD = 'myproject.dashboard.CustomIndexDashboard' +ADMIN_TOOLS_APP_INDEX_DASHBOARD = 'myproject.dashboard.CustomAppIndexDashboard' + +MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media') +MEDIA_URL = '/media/' diff --git a/mysite/urls.py b/mysite/urls.py new file mode 100644 index 0000000..0257aa1 --- /dev/null +++ b/mysite/urls.py @@ -0,0 +1,28 @@ +"""mysite URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.9/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url, include, patterns +from django.contrib import admin +from django.conf import settings +from django.conf.urls.static import static +from django.views.generic.base import TemplateView +urlpatterns = patterns('', + url(r'^admin/', admin.site.urls), + url(r'^$', TemplateView.as_view(template_name='main.html')), + url(r'^blog/', include('blog.urls')), + url(r'^polls/', include('polls.urls', namespace="polls")), + url(r'^imagehost/', include('imagehost.urls')), + url(r'^pfm/', include('pfm.urls')), +) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/mysite/wsgi.py b/mysite/wsgi.py new file mode 100644 index 0000000..f47ba04 --- /dev/null +++ b/mysite/wsgi.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +""" +WSGI config for mysite project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") + +application = get_wsgi_application()