Initial commit
This commit is contained in:
Generated
+26
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="settings.py" />
|
||||
<option name="manageScript" value="manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/apps" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.5.3 virtualenv at ~/Envs/dgblog" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
|
||||
</component>
|
||||
</module>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
</profile>
|
||||
</component>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="useProjectProfile" value="false" />
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
Generated
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectInspectionProfilesVisibleTreeState">
|
||||
<entry key="Project Default">
|
||||
<profile-state>
|
||||
<expanded-state>
|
||||
<State>
|
||||
<id />
|
||||
</State>
|
||||
</expanded-state>
|
||||
<selected-state>
|
||||
<State>
|
||||
<id>AngularJS</id>
|
||||
</State>
|
||||
</selected-state>
|
||||
</profile-state>
|
||||
</entry>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.5.3 virtualenv at ~/Envs/dgblog" project-jdk-type="Python SDK" />
|
||||
<component name="masterDetails">
|
||||
<states>
|
||||
<state key="ScopeChooserConfigurable.UI">
|
||||
<settings>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
</states>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/djangoblog.iml" filepath="$PROJECT_DIR$/.idea/djangoblog.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1 @@
|
||||
__author__ = 'liuhui'
|
||||
@@ -0,0 +1 @@
|
||||
default_app_config = 'blog.apps.BlogConfig'
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
@@ -0,0 +1,34 @@
|
||||
__author__ = 'liuhui'
|
||||
|
||||
import xadmin
|
||||
|
||||
from .models import Article, Category, Link
|
||||
|
||||
|
||||
class ArticleAdmin(object):
|
||||
list_display = ['author', 'category', 'tags', 'title', 'article_from', 'reading_num', 'comment_num', 'like_num', 'is_top', 'rank', 'status', 'create_time', 'update_time']
|
||||
search_fields = ['author', 'category', 'tags', 'title', 'summary', 'article_from', 'content', 'reading_num', 'comment_num', 'like_num', 'is_top', 'rank', 'status']
|
||||
list_filter = ['author__username', 'category__name', 'tags', 'summary', 'article_from', 'content', 'reading_num', 'comment_num', 'like_num', 'is_top', 'rank', 'status', 'create_time', 'update_time']
|
||||
readonly_fields = []
|
||||
style_fields = {'content': 'ueditor'}
|
||||
relfield_style = 'fk-ajax'
|
||||
|
||||
class CategoryAdmin(object):
|
||||
list_display = ['name', 'rank', 'create_time', 'update_time']
|
||||
search_fields = ['name', 'rank']
|
||||
list_filter = ['name', 'rank', 'create_time', 'update_time']
|
||||
|
||||
|
||||
class LinkAdmin(object):
|
||||
list_display = ['name', 'url', 'rank', 'create_time', 'update_time']
|
||||
search_fields = ['name', 'url', 'rank']
|
||||
list_filter = ['name', 'url', 'rank', 'create_time', 'update_time']
|
||||
|
||||
|
||||
xadmin.site.register(Article, ArticleAdmin)
|
||||
xadmin.site.register(Category, CategoryAdmin)
|
||||
xadmin.site.register(Link, LinkAdmin)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BlogConfig(AppConfig):
|
||||
name = 'blog'
|
||||
verbose_name = '博客管理'
|
||||
@@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-07-04 02:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import DjangoUeditor.models
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Article',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=50, verbose_name='标题')),
|
||||
('alias', models.CharField(blank=True, max_length=100, null=True, verbose_name='英文名')),
|
||||
('article_from', models.IntegerField(choices=[(0, '原创'), (1, '转载')], default=0, verbose_name='文章来源')),
|
||||
('summary', models.TextField(verbose_name='摘要')),
|
||||
('tags', models.CharField(blank=True, max_length=100, null=True)),
|
||||
('content', DjangoUeditor.models.UEditorField(default='', verbose_name='正文')),
|
||||
('reading_num', models.IntegerField(default=0, verbose_name='阅读量')),
|
||||
('comment_num', models.IntegerField(default=0, verbose_name='评论数')),
|
||||
('like_num', models.IntegerField(default=0, verbose_name='点赞数')),
|
||||
('is_top', models.BooleanField(default=False, verbose_name='是否置顶')),
|
||||
('rank', models.IntegerField(default=0, verbose_name='排序')),
|
||||
('status', models.IntegerField(choices=[(0, '发表'), (1, '草稿'), (2, '丢弃')], default=0, verbose_name='文章状态')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(blank=True, null=True, verbose_name='修改时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '文章',
|
||||
'verbose_name': '文章',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Category',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=40, verbose_name='类型名称')),
|
||||
('rank', models.IntegerField(default=0, verbose_name='排序')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(blank=True, null=True, verbose_name='修改时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '文章类型',
|
||||
'verbose_name': '文章类型',
|
||||
'ordering': ['rank', '-create_time'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Link',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=40, verbose_name='链接名')),
|
||||
('url', models.URLField(max_length=40, verbose_name='链接地址')),
|
||||
('rank', models.IntegerField(default=0, verbose_name='排序')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(blank=True, null=True, verbose_name='修改时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '友情链接',
|
||||
'verbose_name': '友情链接',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-07-04 02:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('blog', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='author',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.Category', verbose_name='类型'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-07-05 02:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0002_auto_20170704_1041'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='article',
|
||||
old_name='alias',
|
||||
new_name='en_title',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='tags',
|
||||
field=models.CharField(blank=True, help_text='用逗号分隔', max_length=100, null=True, verbose_name='标签'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-07-05 07:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0003_auto_20170705_1056'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='en_title',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,98 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
from djangoblog import settings
|
||||
from django.utils import timezone
|
||||
|
||||
from DjangoUeditor.models import UEditorField
|
||||
|
||||
# Create your models here.
|
||||
|
||||
STATUS = {
|
||||
0: u'发表',
|
||||
1: u'草稿',
|
||||
2: u'丢弃',
|
||||
}
|
||||
|
||||
ARTICLE_FROM = {
|
||||
0: u'原创',
|
||||
1: u'转载',
|
||||
}
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=40, verbose_name=u'类型名称')
|
||||
rank = models.IntegerField(default=0,verbose_name=u'排序')
|
||||
create_time = models.DateTimeField(verbose_name=u'创建时间',default=timezone.now)
|
||||
update_time = models.DateTimeField(verbose_name=u'修改时间',blank=True,null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u'文章类型'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ['rank','-create_time']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Article(models.Model):
|
||||
author = models.ForeignKey(settings.AUTH_USER_MODEL,verbose_name=u'作者')
|
||||
category = models.ForeignKey(Category,verbose_name=u'类型')
|
||||
title = models.CharField(max_length=50,verbose_name=u'标题')
|
||||
#en_title = models.CharField(max_length=100, blank=True, null=True, verbose_name=u'英文名')
|
||||
article_from = models.IntegerField(default=0,choices=ARTICLE_FROM.items(),verbose_name=u'文章来源')
|
||||
summary = models.TextField(verbose_name=u'摘要')
|
||||
tags = models.CharField(max_length=100, null=True, blank=True, verbose_name=u'标签', help_text=u'用逗号分隔')
|
||||
content = UEditorField(verbose_name=u'正文', toolbars='full', width='600', height='300', imagePath='article/ueditor/',filePath='article/ueditor',default='')
|
||||
reading_num = models.IntegerField(default=0,verbose_name=u'阅读量')
|
||||
comment_num = models.IntegerField(default=0,verbose_name=u'评论数')
|
||||
like_num = models.IntegerField(default=0,verbose_name=u'点赞数')
|
||||
|
||||
is_top = models.BooleanField(default=False,verbose_name=u'是否置顶')
|
||||
rank = models.IntegerField(default=0,verbose_name=u'排序')
|
||||
status = models.IntegerField(default=0,choices=STATUS.items(),verbose_name=u'文章状态')
|
||||
|
||||
create_time = models.DateTimeField(default=timezone.now, verbose_name=u'创建时间')
|
||||
update_time = models.DateTimeField(verbose_name=u'修改时间', blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u'文章'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def get_absolute_url(self):
|
||||
from django.core.urlresolvers import reverse
|
||||
return reverse('article-view', args=(self.en_title,))
|
||||
|
||||
def get_tags(self):
|
||||
tags_list = self.tags.split(',')
|
||||
while '' in tags_list:
|
||||
tags_list.remove('')
|
||||
tags_list = tags_list[0:3]
|
||||
|
||||
return tags_list
|
||||
|
||||
def get_category(self):
|
||||
return self.category
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Link(models.Model):
|
||||
name = models.CharField(max_length=40, verbose_name=u'链接名')
|
||||
url = models.URLField(max_length=40, verbose_name=u'链接地址')
|
||||
rank = models.IntegerField(default=0, verbose_name=u'排序')
|
||||
|
||||
create_time = models.DateTimeField(default=timezone.now, verbose_name=u'创建时间')
|
||||
update_time = models.DateTimeField(verbose_name=u'修改时间', blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u'友情链接'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,11 @@
|
||||
__author__ = 'liuhui'
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from blog.views import IndexView, ArticleDetailView, CategoryView, TagView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', IndexView.as_view(), name="index-view"),
|
||||
url(r'^article/(?P<article_id>\d+)/$', ArticleDetailView.as_view(), name='article-view'),
|
||||
url(r'^category/(?P<category_id>\d+)/$', CategoryView.as_view(), name='category-view'),
|
||||
url(r'^tag/(?P<tag>\d+)/$', TagView.as_view(), name='tag-view'),
|
||||
]
|
||||
@@ -0,0 +1,91 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.views.generic import ListView, TemplateView, View, DetailView
|
||||
from blog.models import Article, Category, Link
|
||||
from comments.models import Comment
|
||||
from users.models import UserProfile
|
||||
from djangoblog import settings
|
||||
from django.db.models import Q
|
||||
from django.core.cache import caches
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
cache = caches['default']
|
||||
|
||||
# Create your views here.
|
||||
|
||||
class BaseMixin(object):
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super(BaseMixin, self).get_context_data(**kwargs)
|
||||
context['category_list'] = Category.objects.all()[:10]
|
||||
context['hot_article_list'] = Article.objects.order_by("-reading_num")[:10]
|
||||
context['new_comment_list'] = Comment.objects.order_by("-create_time")[:5]
|
||||
context['hot_user_list'] = UserProfile.objects.order_by("-topic_num")[:5]
|
||||
context['link_list'] = Link.objects.order_by('-create_time')
|
||||
|
||||
colors = ['primary', 'success', 'info', 'warning', 'danger']
|
||||
for index, link in enumerate(context['link_list']):
|
||||
link.color = colors[index % len(colors)]
|
||||
return context
|
||||
|
||||
|
||||
class IndexView(BaseMixin, ListView):
|
||||
template_name = 'blog/index.html'
|
||||
context_object_name = 'article_list'
|
||||
paginate_by = settings.PAGE_NUM
|
||||
|
||||
def get_queryset(self):
|
||||
article_list = Article.objects.filter(status=0).order_by('-create_time')
|
||||
return article_list
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['category_list'] = Category.objects.all().order_by('rank')
|
||||
return super(IndexView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class ArticleDetailView(BaseMixin, DetailView):
|
||||
model = Article
|
||||
template_name = 'blog/article.html'
|
||||
context_object_name = 'article'
|
||||
pk_url_kwarg = 'article_id'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ArticleDetailView, self).get_object()
|
||||
|
||||
#文章点击数 + 1
|
||||
obj.reading_num += 1
|
||||
obj.save()
|
||||
|
||||
return obj
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super(ArticleDetailView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class CategoryView(BaseMixin, ListView):
|
||||
template_name = 'blog/index.html'
|
||||
context_object_name = "article_list"
|
||||
paginate_by = settings.PAGE_NUM
|
||||
|
||||
def get_queryset(self):
|
||||
article_list = Article.objects.filter(category=self.kwargs['category_id'], status=0).order_by('-create_time')
|
||||
return article_list
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super(CategoryView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class TagView(BaseMixin, ListView):
|
||||
template_name = 'blog/index.html'
|
||||
context_object_name = 'article_list'
|
||||
paginate_by = settings.PAGE_NUM
|
||||
|
||||
def get_queryset(self):
|
||||
tag = self.kwargs.get('tag', '')
|
||||
article_list = Article.objects.only('tags').filter(tags__icontains=self.kwargs['tag'], status=0)
|
||||
return article_list
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super(TagView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
default_app_config = 'comments.apps.CommentsConfig'
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
@@ -0,0 +1,14 @@
|
||||
__author__ = 'liuhui'
|
||||
|
||||
import xadmin
|
||||
|
||||
from .models import Comment
|
||||
|
||||
|
||||
class CommentAdmin(object):
|
||||
list_display = ['user', 'article', 'text', 'create_time', 'parentcomment']
|
||||
search_fields = ['user', 'article', 'text', 'parentcomment']
|
||||
list_filter = ['user__username', 'article__title', 'text', 'create_time', 'parentcomment']
|
||||
field = ['user__username', 'article_title']
|
||||
|
||||
xadmin.site.register(Comment, CommentAdmin)
|
||||
@@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CommentsConfig(AppConfig):
|
||||
name = 'comments'
|
||||
verbose_name = '评论信息'
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-07-04 02:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('blog', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Comment',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', models.TextField(verbose_name='评论')),
|
||||
('is_removed', models.BooleanField(default=False)),
|
||||
('likes_count', models.PositiveIntegerField(default=0, verbose_name='点赞数')),
|
||||
('create_time', models.DateTimeField(default=datetime.datetime.now, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(blank=True, null=True, verbose_name='修改时间')),
|
||||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.Article', verbose_name='文章')),
|
||||
('parentcomment', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.Comment', verbose_name='父评论')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '评论',
|
||||
'verbose_name': '评论',
|
||||
'ordering': ['-create_time'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-07-04 02:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('comments', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='comment',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from blog.models import Article
|
||||
from datetime import datetime
|
||||
# Create your models here.
|
||||
|
||||
|
||||
class Comment(models.Model):
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL,verbose_name=u'用户')
|
||||
article = models.ForeignKey(Article,verbose_name=u'文章')
|
||||
text = models.TextField(verbose_name=u'评论')
|
||||
is_removed = models.BooleanField(default=False)
|
||||
parentcomment = models.ForeignKey('self',default=None,blank=True,null=True,verbose_name=u'父评论')
|
||||
likes_count = models.PositiveIntegerField(default=0,verbose_name=u'点赞数')
|
||||
|
||||
create_time = models.DateTimeField(verbose_name=u'创建时间', default=datetime.now)
|
||||
update_time = models.DateTimeField(verbose_name=u'修改时间', blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u'评论'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ['-create_time']
|
||||
|
||||
def __str__(self):
|
||||
return self.article.title
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,9 @@
|
||||
__author__ = 'liuhui'
|
||||
|
||||
|
||||
from django.conf.urls import url, include
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SearchConfig(AppConfig):
|
||||
name = 'apps.search'
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,4 @@
|
||||
from django.shortcuts import render
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
# Create your views here.
|
||||
@@ -0,0 +1 @@
|
||||
default_app_config = "users.apps.UsersConfig"
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
@@ -0,0 +1,44 @@
|
||||
__author__ = 'liuhui'
|
||||
|
||||
import xadmin
|
||||
from xadmin import views
|
||||
|
||||
from .models import Banner, Follower, EmailVerified
|
||||
|
||||
|
||||
class BaseSettings(object):
|
||||
enable_themes = True
|
||||
use_bootswatch = True
|
||||
|
||||
|
||||
class GlobalSettings(object):
|
||||
site_title = "博客后台管理"
|
||||
site_footer = "开发者在线"
|
||||
menu_style = 'accordion'
|
||||
|
||||
|
||||
class FollowerAdmin(object):
|
||||
list_display = ['user_a', 'user_b', 'create_time']
|
||||
search_fields = ['user_a', 'user_b']
|
||||
list_filter = ['user_a__username', 'user_b__username', 'create_time']
|
||||
|
||||
|
||||
class BannerAdmin(object):
|
||||
list_display = ['title', 'image', 'url', 'rank', 'create_time']
|
||||
search_fields = ['title', 'image', 'url', 'rank']
|
||||
list_filter = ['title', 'image', 'url', 'rank', 'create_time']
|
||||
|
||||
|
||||
class EmailVerifiedAdmin(object):
|
||||
list_display = ['user', 'token', 'timestamp']
|
||||
search_fields = ['user', 'token']
|
||||
list_filter = ['user__username', 'token', 'timestamp']
|
||||
|
||||
|
||||
xadmin.site.register(views.BaseAdminView, BaseSettings)
|
||||
xadmin.site.register(views.CommAdminView, GlobalSettings)
|
||||
xadmin.site.register(Follower, FollowerAdmin)
|
||||
xadmin.site.register(Banner, BannerAdmin)
|
||||
xadmin.site.register(EmailVerified, EmailVerifiedAdmin)
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = 'users'
|
||||
verbose_name = '用户信息'
|
||||
@@ -0,0 +1,36 @@
|
||||
__author__ = 'liuhui'
|
||||
|
||||
|
||||
from django import forms
|
||||
from django.contrib import auth
|
||||
from .models import UserProfile
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(label=u'用户名', widget=forms.TextInput(attrs={'placeholder': '用户名或邮箱', 'required': 'required'}), max_length=32, error_messages={'required': u'用户名或邮箱不能为空!'})
|
||||
password = forms.CharField(label=u'密码', widget=forms.PasswordInput(attrs={'placeholder': '密码', 'required': 'required'}), max_length=16, error_messages={'required': u'密码不能为空!'})
|
||||
|
||||
def __init__(self, request=None, *args, **kwargs):
|
||||
self.request = request
|
||||
self.user_cache = None
|
||||
super(LoginForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean_password(self):
|
||||
username = self.cleaned_data.get('username')
|
||||
password = self.cleaned_data.get('password')
|
||||
|
||||
if username and password:
|
||||
self.user_cache = auth.authenticate(username=username, password=password)
|
||||
if self.user_cache is None:
|
||||
raise forms.ValidationError(u'用户名或密码不匹配')
|
||||
return self.cleaned_data
|
||||
|
||||
def get_user(self):
|
||||
return self.user_cache
|
||||
|
||||
|
||||
class RegisterForm(forms.Form):
|
||||
username = forms.CharField()
|
||||
email = forms.EmailField()
|
||||
password = forms.CharField()
|
||||
res_password = forms.CharField()
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-07-04 02:41
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.models
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('username', models.CharField(max_length=32, unique=True, verbose_name='用户名')),
|
||||
('email', models.EmailField(max_length=64, unique=True, verbose_name='邮箱')),
|
||||
('password', models.CharField(max_length=128, verbose_name='密码')),
|
||||
('profile', models.TextField(blank=True, max_length=200, null=True, verbose_name='简介')),
|
||||
('image', models.ImageField(default='/static/images/user/default.jpg', max_length=200, upload_to='user_images/%Y/%m/%d', verbose_name='用户头像')),
|
||||
('au', models.IntegerField(default=0, verbose_name='用户活跃度')),
|
||||
('topic_num', models.IntegerField(default=0, verbose_name='文章数')),
|
||||
('visit_num', models.IntegerField(default=0, verbose_name='访问量')),
|
||||
('comment_num', models.IntegerField(default=0, verbose_name='总评论数')),
|
||||
('email_verified', models.BooleanField(default=False, verbose_name='邮箱是否验证')),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(blank=True, null=True, verbose_name='修改时间')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '用户',
|
||||
'verbose_name': '用户',
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Banner',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=100, verbose_name='标题')),
|
||||
('image', models.ImageField(upload_to='banner_images/%Y/%m/%d', verbose_name='轮播图')),
|
||||
('url', models.URLField(verbose_name='访问地址')),
|
||||
('rank', models.IntegerField(default=100, verbose_name='顺序')),
|
||||
('create_time', models.DateField(default=django.utils.timezone.now, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(blank=True, null=True, verbose_name='修改时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '轮播图',
|
||||
'verbose_name': '轮播图',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EmailVerified',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('token', models.CharField(default=None, max_length=32, verbose_name='Email 验证 token')),
|
||||
('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '邮箱验证',
|
||||
'verbose_name': '邮箱验证',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Follower',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
|
||||
('user_a', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_a', to=settings.AUTH_USER_MODEL, verbose_name='关注者')),
|
||||
('user_b', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_b', to=settings.AUTH_USER_MODEL, verbose_name='被关注者')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': '用户关注',
|
||||
'verbose_name': '用户关注',
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='follower',
|
||||
unique_together=set([('user_a', 'user_b')]),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,157 @@
|
||||
import hashlib
|
||||
import random
|
||||
import string
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, BaseUserManager
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from datetime import datetime
|
||||
|
||||
SALT = getattr(settings, "EMAIL_TOKEN_SALT", "djangoblog")
|
||||
# Create your models here.
|
||||
|
||||
|
||||
class UserManage(BaseUserManager):
|
||||
def create_user(self, username, email, password=None):
|
||||
"""
|
||||
Creates and saves a User
|
||||
"""
|
||||
if not email:
|
||||
raise ValueError("Users must have an email address")
|
||||
if not username:
|
||||
raise ValueError("Users must have an username")
|
||||
|
||||
now = timezone.now()
|
||||
user = self.model(username=username,
|
||||
email=self.normalize_email(email),
|
||||
create_time=now,
|
||||
)
|
||||
user.set_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
def create_superuser(self, username, email, password=None):
|
||||
"""
|
||||
Creates and saves a superuser
|
||||
"""
|
||||
user = self.create_user(username,
|
||||
email,
|
||||
password,
|
||||
)
|
||||
user.is_admin = True
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
|
||||
class UserProfile(AbstractUser):
|
||||
username = models.CharField(max_length=32,verbose_name=u'用户名',unique=True)
|
||||
email = models.EmailField(max_length=64,verbose_name=u'邮箱',unique=True)
|
||||
password = models.CharField(max_length=128,verbose_name=u'密码')
|
||||
profile = models.TextField(max_length=200,verbose_name=u'简介',blank=True,null=True)
|
||||
image = models.ImageField(max_length=200,upload_to='user_images/%Y/%m/%d' ,default="/static/images/user/default.jpg", verbose_name=u'用户头像')
|
||||
au = models.IntegerField(default=0, verbose_name=u'用户活跃度')
|
||||
|
||||
topic_num = models.IntegerField(default=0,verbose_name=u'文章数')
|
||||
visit_num = models.IntegerField(default=0,verbose_name=u'访问量')
|
||||
comment_num = models.IntegerField(default=0,verbose_name=u'总评论数')
|
||||
|
||||
email_verified = models.BooleanField(default=False, verbose_name=u'邮箱是否验证')
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
create_time = models.DateTimeField(verbose_name=u'创建时间',default=timezone.now)
|
||||
update_time = models.DateTimeField(verbose_name=u'修改时间', blank=True, null=True)
|
||||
|
||||
USERNAME_FIELD = 'username'
|
||||
REQUIRED_FIELDS = ['email']
|
||||
|
||||
class Meta:
|
||||
verbose_name = u'用户'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def is_email_verified(self):
|
||||
return self.email_verified
|
||||
|
||||
def get_username(self):
|
||||
return self.username
|
||||
|
||||
def get_email(self):
|
||||
return self.email
|
||||
|
||||
def get_full_name(self):
|
||||
return self.email
|
||||
|
||||
def get_short_name(self):
|
||||
return self.username
|
||||
|
||||
def has_perm(self, perm, obj=None):
|
||||
return True
|
||||
|
||||
def has_module_perms(self, app_label):
|
||||
return True
|
||||
|
||||
def calculate_au(self):
|
||||
self.au = self.topic_num * 5 + self.visit_num * 1 + self.comment_num * 1
|
||||
return self.au
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
|
||||
class Banner(models.Model):
|
||||
title = models.CharField(max_length=100, verbose_name=u'标题')
|
||||
image = models.ImageField(upload_to='banner_images/%Y/%m/%d', max_length=100, verbose_name=u'轮播图')
|
||||
url = models.URLField(max_length=200, verbose_name=u'访问地址')
|
||||
rank = models.IntegerField(default=100, verbose_name=u'顺序')
|
||||
create_time = models.DateField(default=timezone.now, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(verbose_name=u'修改时间', blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '轮播图'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Follower(models.Model):
|
||||
user_a = models.ForeignKey(UserProfile, related_name="user_a", verbose_name=u'关注者')
|
||||
user_b = models.ForeignKey(UserProfile, related_name="user_b", verbose_name=u'被关注者')
|
||||
create_time = models.DateTimeField(default=timezone.now, verbose_name=u'创建时间')
|
||||
|
||||
class Meta:
|
||||
unique_together = ('user_a', 'user_b')
|
||||
verbose_name = u'用户关注'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return "%s following %s" % (self.user_a, self.user_b)
|
||||
|
||||
|
||||
class EmailVerified(models.Model):
|
||||
user = models.OneToOneField(UserProfile, related_name='user', verbose_name=u'用户')
|
||||
token = models.CharField(max_length=32, default=None, verbose_name=u"Email 验证 token")
|
||||
timestamp = models.DateTimeField(default=timezone.now)
|
||||
|
||||
class Meta:
|
||||
verbose_name = u'邮箱验证'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return "%s@%s" % (self.user, self.token)
|
||||
|
||||
def generate_token(self):
|
||||
year = self.timestamp.year
|
||||
month = self.timestamp.month
|
||||
day = self.timestamp.day
|
||||
date = "%s-%s-%s" % (year, month, day)
|
||||
token = hashlib.md5(str(self.user.id) + self.user.username + self.ran_str() + date).hexdigest()
|
||||
|
||||
def ran_str(self):
|
||||
salt = ''.join(random.sample(string.ascii_letters + string.digits, 8))
|
||||
return salt + SALT
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,12 @@
|
||||
__author__ = 'liuhui'
|
||||
|
||||
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from users.views import LoginView
|
||||
from users import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^login/$', LoginView.as_view(), name='login'),
|
||||
url(r'^logout/$', views.logout, name='logout'),
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils import timezone
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.views.generic import View
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.cache import cache
|
||||
from django.contrib import messages
|
||||
from django.utils import timezone
|
||||
|
||||
from .forms import LoginForm
|
||||
from .models import UserProfile
|
||||
|
||||
import datetime
|
||||
import re
|
||||
import json
|
||||
import random
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
class LoginView(View):
|
||||
def get(self, request):
|
||||
return render(request, 'users/login.html')
|
||||
def post(self, request):
|
||||
if request.method == "POST":
|
||||
login_form = LoginForm(request.POST)
|
||||
if login_form.is_valid():
|
||||
username = login_form.cleaned_data['username']
|
||||
password = login_form.cleaned_data['password']
|
||||
user = authenticate(username=username, password=password)
|
||||
if user is not None:
|
||||
auth_login(request, user)
|
||||
return HttpResponsePermanentRedirect(reverse('index-view'))
|
||||
return render(request, 'users/login.html', {'msg': '用户名或密码错误'})
|
||||
return render(request, 'users/login.html', {'form_errors': login_form.errors})
|
||||
|
||||
|
||||
def logout(request):
|
||||
auth_logout(request)
|
||||
return HttpResponseRedirect(reverse("index-view"))
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[xadmin-core.django]
|
||||
file_filter = locale/<lang>/LC_MESSAGES/django.po
|
||||
source_file = locale/en/LC_MESSAGES/django.po
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
||||
[xadmin-core.djangojs]
|
||||
file_filter = locale/<lang>/LC_MESSAGES/djangojs.po
|
||||
source_file = locale/en/LC_MESSAGES/djangojs.po
|
||||
source_lang = en
|
||||
type = PO
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
|
||||
VERSION = (0,6,0)
|
||||
|
||||
from xadmin.sites import AdminSite, site
|
||||
|
||||
class Settings(object):
|
||||
pass
|
||||
|
||||
|
||||
def autodiscover():
|
||||
"""
|
||||
Auto-discover INSTALLED_APPS admin.py modules and fail silently when
|
||||
not present. This forces an import on them to register any admin bits they
|
||||
may want.
|
||||
"""
|
||||
|
||||
from importlib import import_module
|
||||
from django.conf import settings
|
||||
from django.utils.module_loading import module_has_submodule
|
||||
setattr(settings, 'CRISPY_TEMPLATE_PACK', 'bootstrap3')
|
||||
setattr(settings, 'CRISPY_CLASS_CONVERTERS', {
|
||||
"textinput": "textinput textInput form-control",
|
||||
"fileinput": "fileinput fileUpload form-control",
|
||||
"passwordinput": "textinput textInput form-control",
|
||||
})
|
||||
|
||||
from xadmin.views import register_builtin_views
|
||||
register_builtin_views(site)
|
||||
|
||||
# load xadmin settings from XADMIN_CONF module
|
||||
try:
|
||||
xadmin_conf = getattr(settings, 'XADMIN_CONF', 'xadmin_conf.py')
|
||||
conf_mod = import_module(xadmin_conf)
|
||||
except Exception:
|
||||
conf_mod = None
|
||||
|
||||
if conf_mod:
|
||||
for key in dir(conf_mod):
|
||||
setting = getattr(conf_mod, key)
|
||||
try:
|
||||
if issubclass(setting, Settings):
|
||||
site.register_settings(setting.__name__, setting)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from xadmin.plugins import register_builtin_plugins
|
||||
register_builtin_plugins(site)
|
||||
|
||||
for app in settings.INSTALLED_APPS:
|
||||
mod = import_module(app)
|
||||
# Attempt to import the app's admin module.
|
||||
try:
|
||||
before_import_registry = site.copy_registry()
|
||||
import_module('%s.adminx' % app)
|
||||
except:
|
||||
# Reset the model registry to the state before the last import as
|
||||
# this import will have to reoccur on the next request and this
|
||||
# could raise NotRegistered and AlreadyRegistered exceptions
|
||||
# (see #8245).
|
||||
site.restore_registry(before_import_registry)
|
||||
|
||||
# Decide whether to bubble up this error. If the app just
|
||||
# doesn't have an admin module, we can ignore the error
|
||||
# attempting to import it, otherwise we want it to bubble up.
|
||||
if module_has_submodule(mod, 'adminx'):
|
||||
raise
|
||||
|
||||
default_app_config = 'xadmin.apps.XAdminConfig'
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
from __future__ import absolute_import
|
||||
import xadmin
|
||||
from .models import UserSettings, Log
|
||||
from xadmin.layout import *
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||
|
||||
class UserSettingsAdmin(object):
|
||||
model_icon = 'fa fa-cog'
|
||||
hidden_menu = True
|
||||
|
||||
xadmin.site.register(UserSettings, UserSettingsAdmin)
|
||||
|
||||
class LogAdmin(object):
|
||||
|
||||
def link(self, instance):
|
||||
if instance.content_type and instance.object_id and instance.action_flag != 'delete':
|
||||
admin_url = self.get_admin_url('%s_%s_change' % (instance.content_type.app_label, instance.content_type.model),
|
||||
instance.object_id)
|
||||
return "<a href='%s'>%s</a>" % (admin_url, _('Admin Object'))
|
||||
else:
|
||||
return ''
|
||||
link.short_description = ""
|
||||
link.allow_tags = True
|
||||
link.is_column = False
|
||||
|
||||
list_display = ('action_time', 'user', 'ip_addr', '__str__', 'link')
|
||||
list_filter = ['user', 'action_time']
|
||||
search_fields = ['ip_addr', 'message']
|
||||
model_icon = 'fa fa-cog'
|
||||
|
||||
xadmin.site.register(Log, LogAdmin)
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
from django.apps import AppConfig
|
||||
from django.core import checks
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import xadmin
|
||||
|
||||
|
||||
class XAdminConfig(AppConfig):
|
||||
"""Simple AppConfig which does not do automatic discovery."""
|
||||
|
||||
name = 'xadmin'
|
||||
verbose_name = _("Administration")
|
||||
|
||||
def ready(self):
|
||||
self.module.autodiscover()
|
||||
setattr(xadmin,'site',xadmin.site)
|
||||
Executable
+571
@@ -0,0 +1,571 @@
|
||||
from __future__ import absolute_import
|
||||
from django.db import models
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.template.loader import get_template
|
||||
from django.template.context import Context
|
||||
from django.utils import six
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.html import escape,format_html
|
||||
from django.utils.text import Truncator
|
||||
from django.core.cache import cache, caches
|
||||
|
||||
from xadmin.views.list import EMPTY_CHANGELIST_VALUE
|
||||
from xadmin.util import is_related_field,is_related_field2
|
||||
import datetime
|
||||
|
||||
FILTER_PREFIX = '_p_'
|
||||
SEARCH_VAR = '_q_'
|
||||
|
||||
from .util import (get_model_from_relation,
|
||||
reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
|
||||
|
||||
|
||||
class BaseFilter(object):
|
||||
title = None
|
||||
template = 'xadmin/filters/list.html'
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
pass
|
||||
|
||||
def __init__(self, request, params, model, admin_view):
|
||||
self.used_params = {}
|
||||
self.request = request
|
||||
self.params = params
|
||||
self.model = model
|
||||
self.admin_view = admin_view
|
||||
|
||||
if self.title is None:
|
||||
raise ImproperlyConfigured(
|
||||
"The filter '%s' does not specify "
|
||||
"a 'title'." % self.__class__.__name__)
|
||||
|
||||
def query_string(self, new_params=None, remove=None):
|
||||
return self.admin_view.get_query_string(new_params, remove)
|
||||
|
||||
def form_params(self):
|
||||
arr = map(lambda k: FILTER_PREFIX + k, self.used_params.keys())
|
||||
if six.PY3:
|
||||
arr = list(arr)
|
||||
return self.admin_view.get_form_params(remove=arr)
|
||||
|
||||
def has_output(self):
|
||||
"""
|
||||
Returns True if some choices would be output for this filter.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def is_used(self):
|
||||
return len(self.used_params) > 0
|
||||
|
||||
def do_filte(self, queryset):
|
||||
"""
|
||||
Returns the filtered queryset.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_context(self):
|
||||
return {'title': self.title, 'spec': self, 'form_params': self.form_params()}
|
||||
|
||||
def __str__(self):
|
||||
tpl = get_template(self.template)
|
||||
return mark_safe(tpl.render(context=self.get_context()))
|
||||
|
||||
|
||||
class FieldFilterManager(object):
|
||||
_field_list_filters = []
|
||||
_take_priority_index = 0
|
||||
|
||||
def register(self, list_filter_class, take_priority=False):
|
||||
if take_priority:
|
||||
# This is to allow overriding the default filters for certain types
|
||||
# of fields with some custom filters. The first found in the list
|
||||
# is used in priority.
|
||||
self._field_list_filters.insert(
|
||||
self._take_priority_index, list_filter_class)
|
||||
self._take_priority_index += 1
|
||||
else:
|
||||
self._field_list_filters.append(list_filter_class)
|
||||
return list_filter_class
|
||||
|
||||
def create(self, field, request, params, model, admin_view, field_path):
|
||||
for list_filter_class in self._field_list_filters:
|
||||
if not list_filter_class.test(field, request, params, model, admin_view, field_path):
|
||||
continue
|
||||
return list_filter_class(field, request, params,
|
||||
model, admin_view, field_path=field_path)
|
||||
|
||||
manager = FieldFilterManager()
|
||||
|
||||
|
||||
class FieldFilter(BaseFilter):
|
||||
|
||||
lookup_formats = {}
|
||||
|
||||
def __init__(self, field, request, params, model, admin_view, field_path):
|
||||
self.field = field
|
||||
self.field_path = field_path
|
||||
self.title = getattr(field, 'verbose_name', field_path)
|
||||
self.context_params = {}
|
||||
|
||||
super(FieldFilter, self).__init__(request, params, model, admin_view)
|
||||
|
||||
for name, format in self.lookup_formats.items():
|
||||
p = format % field_path
|
||||
self.context_params["%s_name" % name] = FILTER_PREFIX + p
|
||||
if p in params:
|
||||
value = prepare_lookup_value(p, params.pop(p))
|
||||
self.used_params[p] = value
|
||||
self.context_params["%s_val" % name] = value
|
||||
else:
|
||||
self.context_params["%s_val" % name] = ''
|
||||
|
||||
arr = map(
|
||||
lambda kv: setattr(self, 'lookup_' + kv[0], kv[1]),
|
||||
self.context_params.items()
|
||||
)
|
||||
if six.PY3:
|
||||
list(arr)
|
||||
|
||||
def get_context(self):
|
||||
context = super(FieldFilter, self).get_context()
|
||||
context.update(self.context_params)
|
||||
obj = map(lambda k: FILTER_PREFIX + k, self.used_params.keys())
|
||||
if six.PY3:
|
||||
obj = list(obj)
|
||||
context['remove_url'] = self.query_string({}, obj)
|
||||
return context
|
||||
|
||||
def has_output(self):
|
||||
return True
|
||||
|
||||
def do_filte(self, queryset):
|
||||
return queryset.filter(**self.used_params)
|
||||
|
||||
|
||||
class ListFieldFilter(FieldFilter):
|
||||
template = 'xadmin/filters/list.html'
|
||||
|
||||
def get_context(self):
|
||||
context = super(ListFieldFilter, self).get_context()
|
||||
context['choices'] = list(self.choices())
|
||||
return context
|
||||
|
||||
|
||||
@manager.register
|
||||
class BooleanFieldListFilter(ListFieldFilter):
|
||||
lookup_formats = {'exact': '%s__exact', 'isnull': '%s__isnull'}
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return isinstance(field, (models.BooleanField, models.NullBooleanField))
|
||||
|
||||
def choices(self):
|
||||
for lookup, title in (
|
||||
('', _('All')),
|
||||
('1', _('Yes')),
|
||||
('0', _('No')),
|
||||
):
|
||||
yield {
|
||||
'selected': (
|
||||
self.lookup_exact_val == lookup
|
||||
and not self.lookup_isnull_val
|
||||
),
|
||||
'query_string': self.query_string(
|
||||
{self.lookup_exact_name: lookup},
|
||||
[self.lookup_isnull_name],
|
||||
),
|
||||
'display': title,
|
||||
}
|
||||
if isinstance(self.field, models.NullBooleanField):
|
||||
yield {
|
||||
'selected': self.lookup_isnull_val == 'True',
|
||||
'query_string': self.query_string(
|
||||
{self.lookup_isnull_name: 'True'},
|
||||
[self.lookup_exact_name],
|
||||
),
|
||||
'display': _('Unknown'),
|
||||
}
|
||||
|
||||
|
||||
@manager.register
|
||||
class ChoicesFieldListFilter(ListFieldFilter):
|
||||
lookup_formats = {'exact': '%s__exact'}
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return bool(field.choices)
|
||||
|
||||
def choices(self):
|
||||
yield {
|
||||
'selected': self.lookup_exact_val is '',
|
||||
'query_string': self.query_string({}, [self.lookup_exact_name]),
|
||||
'display': _('All')
|
||||
}
|
||||
for lookup, title in self.field.flatchoices:
|
||||
yield {
|
||||
'selected': smart_text(lookup) == self.lookup_exact_val,
|
||||
'query_string': self.query_string({self.lookup_exact_name: lookup}),
|
||||
'display': title,
|
||||
}
|
||||
|
||||
|
||||
@manager.register
|
||||
class TextFieldListFilter(FieldFilter):
|
||||
template = 'xadmin/filters/char.html'
|
||||
lookup_formats = {'in': '%s__in', 'search': '%s__contains'}
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return (
|
||||
isinstance(field, models.CharField)
|
||||
and field.max_length > 20
|
||||
or isinstance(field, models.TextField)
|
||||
)
|
||||
|
||||
|
||||
@manager.register
|
||||
class NumberFieldListFilter(FieldFilter):
|
||||
template = 'xadmin/filters/number.html'
|
||||
lookup_formats = {'equal': '%s__exact', 'lt': '%s__lt', 'gt': '%s__gt',
|
||||
'ne': '%s__ne', 'lte': '%s__lte', 'gte': '%s__gte',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return isinstance(field, (models.DecimalField, models.FloatField, models.IntegerField))
|
||||
|
||||
def do_filte(self, queryset):
|
||||
params = self.used_params.copy()
|
||||
ne_key = '%s__ne' % self.field_path
|
||||
if ne_key in params:
|
||||
queryset = queryset.exclude(
|
||||
**{self.field_path: params.pop(ne_key)})
|
||||
return queryset.filter(**params)
|
||||
|
||||
|
||||
@manager.register
|
||||
class DateFieldListFilter(ListFieldFilter):
|
||||
template = 'xadmin/filters/date.html'
|
||||
lookup_formats = {'since': '%s__gte', 'until': '%s__lt',
|
||||
'year': '%s__year', 'month': '%s__month', 'day': '%s__day',
|
||||
'isnull': '%s__isnull'}
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return isinstance(field, models.DateField)
|
||||
|
||||
def __init__(self, field, request, params, model, admin_view, field_path):
|
||||
self.field_generic = '%s__' % field_path
|
||||
self.date_params = dict([(FILTER_PREFIX + k, v) for k, v in params.items()
|
||||
if k.startswith(self.field_generic)])
|
||||
|
||||
super(DateFieldListFilter, self).__init__(
|
||||
field, request, params, model, admin_view, field_path)
|
||||
|
||||
now = timezone.now()
|
||||
# When time zone support is enabled, convert "now" to the user's time
|
||||
# zone so Django's definition of "Today" matches what the user expects.
|
||||
if now.tzinfo is not None:
|
||||
current_tz = timezone.get_current_timezone()
|
||||
now = now.astimezone(current_tz)
|
||||
if hasattr(current_tz, 'normalize'):
|
||||
# available for pytz time zones
|
||||
now = current_tz.normalize(now)
|
||||
|
||||
if isinstance(field, models.DateTimeField):
|
||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
else: # field is a models.DateField
|
||||
today = now.date()
|
||||
tomorrow = today + datetime.timedelta(days=1)
|
||||
|
||||
self.links = (
|
||||
(_('Any date'), {}),
|
||||
(_('Has date'), {
|
||||
self.lookup_isnull_name: False
|
||||
}),
|
||||
(_('Has no date'), {
|
||||
self.lookup_isnull_name: 'True'
|
||||
}),
|
||||
(_('Today'), {
|
||||
self.lookup_since_name: str(today),
|
||||
self.lookup_until_name: str(tomorrow),
|
||||
}),
|
||||
(_('Past 7 days'), {
|
||||
self.lookup_since_name: str(today - datetime.timedelta(days=7)),
|
||||
self.lookup_until_name: str(tomorrow),
|
||||
}),
|
||||
(_('This month'), {
|
||||
self.lookup_since_name: str(today.replace(day=1)),
|
||||
self.lookup_until_name: str(tomorrow),
|
||||
}),
|
||||
(_('This year'), {
|
||||
self.lookup_since_name: str(today.replace(month=1, day=1)),
|
||||
self.lookup_until_name: str(tomorrow),
|
||||
}),
|
||||
)
|
||||
|
||||
def get_context(self):
|
||||
context = super(DateFieldListFilter, self).get_context()
|
||||
context['choice_selected'] = bool(self.lookup_year_val) or bool(self.lookup_month_val) \
|
||||
or bool(self.lookup_day_val)
|
||||
return context
|
||||
|
||||
def choices(self):
|
||||
for title, param_dict in self.links:
|
||||
yield {
|
||||
'selected': self.date_params == param_dict,
|
||||
'query_string': self.query_string(
|
||||
param_dict, [FILTER_PREFIX + self.field_generic]),
|
||||
'display': title,
|
||||
}
|
||||
|
||||
|
||||
@manager.register
|
||||
class RelatedFieldSearchFilter(FieldFilter):
|
||||
template = 'xadmin/filters/fk_search.html'
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
if not is_related_field2(field):
|
||||
return False
|
||||
related_modeladmin = admin_view.admin_site._registry.get(
|
||||
get_model_from_relation(field))
|
||||
return related_modeladmin and getattr(related_modeladmin, 'relfield_style', None) in ('fk-ajax', 'fk-select')
|
||||
|
||||
def __init__(self, field, request, params, model, model_admin, field_path):
|
||||
other_model = get_model_from_relation(field)
|
||||
if hasattr(field, 'rel'):
|
||||
rel_name = field.rel.get_related_field().name
|
||||
else:
|
||||
rel_name = other_model._meta.pk.name
|
||||
|
||||
self.lookup_formats = {'in': '%%s__%s__in' % rel_name,'exact': '%%s__%s__exact' % rel_name}
|
||||
super(RelatedFieldSearchFilter, self).__init__(
|
||||
field, request, params, model, model_admin, field_path)
|
||||
|
||||
related_modeladmin = self.admin_view.admin_site._registry.get(other_model)
|
||||
self.relfield_style = related_modeladmin.relfield_style
|
||||
|
||||
if hasattr(field, 'verbose_name'):
|
||||
self.lookup_title = field.verbose_name
|
||||
else:
|
||||
self.lookup_title = other_model._meta.verbose_name
|
||||
self.title = self.lookup_title
|
||||
self.search_url = model_admin.get_admin_url('%s_%s_changelist' % (
|
||||
other_model._meta.app_label, other_model._meta.model_name))
|
||||
self.label = self.label_for_value(other_model, rel_name, self.lookup_exact_val) if self.lookup_exact_val else ""
|
||||
self.choices = '?'
|
||||
if field.rel.limit_choices_to:
|
||||
for i in list(field.rel.limit_choices_to):
|
||||
self.choices += "&_p_%s=%s" % (i, field.rel.limit_choices_to[i])
|
||||
self.choices = format_html(self.choices)
|
||||
|
||||
def label_for_value(self, other_model, rel_name, value):
|
||||
try:
|
||||
obj = other_model._default_manager.get(**{rel_name: value})
|
||||
return '%s' % escape(Truncator(obj).words(14, truncate='...'))
|
||||
except (ValueError, other_model.DoesNotExist):
|
||||
return ""
|
||||
|
||||
def get_context(self):
|
||||
context = super(RelatedFieldSearchFilter, self).get_context()
|
||||
context['search_url'] = self.search_url
|
||||
context['label'] = self.label
|
||||
context['choices'] = self.choices
|
||||
context['relfield_style'] = self.relfield_style
|
||||
return context
|
||||
|
||||
|
||||
@manager.register
|
||||
class RelatedFieldListFilter(ListFieldFilter):
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return is_related_field2(field)
|
||||
|
||||
def __init__(self, field, request, params, model, model_admin, field_path):
|
||||
other_model = get_model_from_relation(field)
|
||||
if hasattr(field, 'rel'):
|
||||
rel_name = field.rel.get_related_field().name
|
||||
else:
|
||||
rel_name = other_model._meta.pk.name
|
||||
|
||||
self.lookup_formats = {'in': '%%s__%s__in' % rel_name,'exact': '%%s__%s__exact' %
|
||||
rel_name, 'isnull': '%s__isnull'}
|
||||
self.lookup_choices = field.get_choices(include_blank=False)
|
||||
super(RelatedFieldListFilter, self).__init__(
|
||||
field, request, params, model, model_admin, field_path)
|
||||
|
||||
if hasattr(field, 'verbose_name'):
|
||||
self.lookup_title = field.verbose_name
|
||||
else:
|
||||
self.lookup_title = other_model._meta.verbose_name
|
||||
self.title = self.lookup_title
|
||||
|
||||
def has_output(self):
|
||||
if (is_related_field(self.field)
|
||||
and self.field.field.null or hasattr(self.field, 'rel')
|
||||
and self.field.null):
|
||||
extra = 1
|
||||
else:
|
||||
extra = 0
|
||||
return len(self.lookup_choices) + extra > 1
|
||||
|
||||
def expected_parameters(self):
|
||||
return [self.lookup_kwarg, self.lookup_kwarg_isnull]
|
||||
|
||||
def choices(self):
|
||||
yield {
|
||||
'selected': self.lookup_exact_val == '' and not self.lookup_isnull_val,
|
||||
'query_string': self.query_string({},
|
||||
[self.lookup_exact_name, self.lookup_isnull_name]),
|
||||
'display': _('All'),
|
||||
}
|
||||
for pk_val, val in self.lookup_choices:
|
||||
yield {
|
||||
'selected': self.lookup_exact_val == smart_text(pk_val),
|
||||
'query_string': self.query_string({
|
||||
self.lookup_exact_name: pk_val,
|
||||
}, [self.lookup_isnull_name]),
|
||||
'display': val,
|
||||
}
|
||||
if (is_related_field(self.field)
|
||||
and self.field.field.null or hasattr(self.field, 'rel')
|
||||
and self.field.null):
|
||||
yield {
|
||||
'selected': bool(self.lookup_isnull_val),
|
||||
'query_string': self.query_string({
|
||||
self.lookup_isnull_name: 'True',
|
||||
}, [self.lookup_exact_name]),
|
||||
'display': EMPTY_CHANGELIST_VALUE,
|
||||
}
|
||||
|
||||
@manager.register
|
||||
class MultiSelectFieldListFilter(ListFieldFilter):
|
||||
""" Delegates the filter to the default filter and ors the results of each
|
||||
|
||||
Lists the distinct values of each field as a checkbox
|
||||
Uses the default spec for each
|
||||
|
||||
"""
|
||||
template = 'xadmin/filters/checklist.html'
|
||||
lookup_formats = {'in': '%s__in'}
|
||||
cache_config = {'enabled':False,'key':'quickfilter_%s','timeout':3600,'cache':'default'}
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return True
|
||||
|
||||
def get_cached_choices(self):
|
||||
if not self.cache_config['enabled']:
|
||||
return None
|
||||
c = caches(self.cache_config['cache'])
|
||||
return c.get(self.cache_config['key']%self.field_path)
|
||||
|
||||
def set_cached_choices(self,choices):
|
||||
if not self.cache_config['enabled']:
|
||||
return
|
||||
c = caches(self.cache_config['cache'])
|
||||
return c.set(self.cache_config['key']%self.field_path,choices)
|
||||
|
||||
def __init__(self, field, request, params, model, model_admin, field_path,field_order_by=None,field_limit=None,sort_key=None,cache_config=None):
|
||||
super(MultiSelectFieldListFilter,self).__init__(field, request, params, model, model_admin, field_path)
|
||||
|
||||
# Check for it in the cachce
|
||||
if cache_config is not None and type(cache_config)==dict:
|
||||
self.cache_config.update(cache_config)
|
||||
|
||||
if self.cache_config['enabled']:
|
||||
self.field_path = field_path
|
||||
choices = self.get_cached_choices()
|
||||
if choices:
|
||||
self.lookup_choices = choices
|
||||
return
|
||||
|
||||
# Else rebuild it
|
||||
queryset = self.admin_view.queryset().exclude(**{"%s__isnull"%field_path:True}).values_list(field_path, flat=True).distinct()
|
||||
#queryset = self.admin_view.queryset().distinct(field_path).exclude(**{"%s__isnull"%field_path:True})
|
||||
|
||||
if field_order_by is not None:
|
||||
# Do a subquery to order the distinct set
|
||||
queryset = self.admin_view.queryset().filter(id__in=queryset).order_by(field_order_by)
|
||||
|
||||
if field_limit is not None and type(field_limit)==int and queryset.count()>field_limit:
|
||||
queryset = queryset[:field_limit]
|
||||
|
||||
self.lookup_choices = [str(it) for it in queryset.values_list(field_path,flat=True) if str(it).strip()!=""]
|
||||
if sort_key is not None:
|
||||
self.lookup_choices = sorted(self.lookup_choices,key=sort_key)
|
||||
|
||||
if self.cache_config['enabled']:
|
||||
self.set_cached_choices(self.lookup_choices)
|
||||
|
||||
def choices(self):
|
||||
self.lookup_in_val = (type(self.lookup_in_val) in (tuple,list)) and self.lookup_in_val or list(self.lookup_in_val)
|
||||
yield {
|
||||
'selected': len(self.lookup_in_val) == 0,
|
||||
'query_string': self.query_string({},[self.lookup_in_name]),
|
||||
'display': _('All'),
|
||||
}
|
||||
for val in self.lookup_choices:
|
||||
yield {
|
||||
'selected': smart_text(val) in self.lookup_in_val,
|
||||
'query_string': self.query_string({self.lookup_in_name: ",".join([val]+self.lookup_in_val),}),
|
||||
'remove_query_string': self.query_string({self.lookup_in_name: ",".join([v for v in self.lookup_in_val if v != val]),}),
|
||||
'display': val,
|
||||
}
|
||||
|
||||
@manager.register
|
||||
class AllValuesFieldListFilter(ListFieldFilter):
|
||||
lookup_formats = {'exact': '%s__exact', 'isnull': '%s__isnull'}
|
||||
|
||||
@classmethod
|
||||
def test(cls, field, request, params, model, admin_view, field_path):
|
||||
return True
|
||||
|
||||
def __init__(self, field, request, params, model, admin_view, field_path):
|
||||
parent_model, reverse_path = reverse_field_path(model, field_path)
|
||||
queryset = parent_model._default_manager.all()
|
||||
# optional feature: limit choices base on existing relationships
|
||||
# queryset = queryset.complex_filter(
|
||||
# {'%s__isnull' % reverse_path: False})
|
||||
limit_choices_to = get_limit_choices_to_from_path(model, field_path)
|
||||
queryset = queryset.filter(limit_choices_to)
|
||||
|
||||
self.lookup_choices = (queryset
|
||||
.distinct()
|
||||
.order_by(field.name)
|
||||
.values_list(field.name, flat=True))
|
||||
super(AllValuesFieldListFilter, self).__init__(
|
||||
field, request, params, model, admin_view, field_path)
|
||||
|
||||
def choices(self):
|
||||
yield {
|
||||
'selected': (self.lookup_exact_val is '' and self.lookup_isnull_val is ''),
|
||||
'query_string': self.query_string({}, [self.lookup_exact_name, self.lookup_isnull_name]),
|
||||
'display': _('All'),
|
||||
}
|
||||
include_none = False
|
||||
for val in self.lookup_choices:
|
||||
if val is None:
|
||||
include_none = True
|
||||
continue
|
||||
val = smart_text(val)
|
||||
yield {
|
||||
'selected': self.lookup_exact_val == val,
|
||||
'query_string': self.query_string({self.lookup_exact_name: val},
|
||||
[self.lookup_isnull_name]),
|
||||
'display': val,
|
||||
}
|
||||
if include_none:
|
||||
yield {
|
||||
'selected': bool(self.lookup_isnull_val),
|
||||
'query_string': self.query_string({self.lookup_isnull_name: 'True'},
|
||||
[self.lookup_exact_name]),
|
||||
'display': EMPTY_CHANGELIST_VALUE,
|
||||
}
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
from django import forms
|
||||
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
|
||||
from django.utils.translation import ugettext_lazy, ugettext as _
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
ERROR_MESSAGE = ugettext_lazy("Please enter the correct username and password "
|
||||
"for a staff account. Note that both fields are case-sensitive.")
|
||||
|
||||
|
||||
class AdminAuthenticationForm(AuthenticationForm):
|
||||
"""
|
||||
A custom authentication form used in the admin app.
|
||||
|
||||
"""
|
||||
this_is_the_login_form = forms.BooleanField(
|
||||
widget=forms.HiddenInput, initial=1,
|
||||
error_messages={'required': ugettext_lazy("Please log in again, because your session has expired.")})
|
||||
|
||||
def clean(self):
|
||||
username = self.cleaned_data.get('username')
|
||||
password = self.cleaned_data.get('password')
|
||||
message = ERROR_MESSAGE
|
||||
|
||||
if username and password:
|
||||
self.user_cache = authenticate(
|
||||
username=username, password=password)
|
||||
if self.user_cache is None:
|
||||
if u'@' in username:
|
||||
User = get_user_model()
|
||||
# Mistakenly entered e-mail address instead of username? Look it up.
|
||||
try:
|
||||
user = User.objects.get(email=username)
|
||||
except (User.DoesNotExist, User.MultipleObjectsReturned):
|
||||
# Nothing to do here, moving along.
|
||||
pass
|
||||
else:
|
||||
if user.check_password(password):
|
||||
message = _("Your e-mail address is not your username."
|
||||
" Try '%s' instead.") % user.username
|
||||
raise forms.ValidationError(message)
|
||||
elif not self.user_cache.is_active or not self.user_cache.is_staff:
|
||||
raise forms.ValidationError(message)
|
||||
return self.cleaned_data
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import *
|
||||
from crispy_forms.bootstrap import *
|
||||
from crispy_forms.utils import render_field, flatatt, TEMPLATE_PACK
|
||||
|
||||
from crispy_forms import layout
|
||||
from crispy_forms import bootstrap
|
||||
|
||||
import math
|
||||
|
||||
|
||||
class Fieldset(layout.Fieldset):
|
||||
template = "xadmin/layout/fieldset.html"
|
||||
|
||||
def __init__(self, legend, *fields, **kwargs):
|
||||
self.description = kwargs.pop('description', None)
|
||||
self.collapsed = kwargs.pop('collapsed', None)
|
||||
super(Fieldset, self).__init__(legend, *fields, **kwargs)
|
||||
|
||||
|
||||
class Row(layout.Div):
|
||||
|
||||
def __init__(self, *fields, **kwargs):
|
||||
css_class = 'form-inline form-group'
|
||||
new_fields = [self.convert_field(f, len(fields)) for f in fields]
|
||||
super(Row, self).__init__(css_class=css_class, *new_fields, **kwargs)
|
||||
|
||||
def convert_field(self, f, counts):
|
||||
col_class = "col-sm-%d" % int(math.ceil(12 / counts))
|
||||
if not (isinstance(f, Field) or issubclass(f.__class__, Field)):
|
||||
f = layout.Field(f)
|
||||
if f.wrapper_class:
|
||||
f.wrapper_class += " %s" % col_class
|
||||
else:
|
||||
f.wrapper_class = col_class
|
||||
return f
|
||||
|
||||
|
||||
class Col(layout.Column):
|
||||
|
||||
def __init__(self, id, *fields, **kwargs):
|
||||
css_class = ['column', 'form-column', id, 'col col-sm-%d' %
|
||||
kwargs.get('span', 6)]
|
||||
if kwargs.get('horizontal'):
|
||||
css_class.append('form-horizontal')
|
||||
super(Col, self).__init__(css_class=' '.join(css_class), *
|
||||
fields, **kwargs)
|
||||
|
||||
|
||||
class Main(layout.Column):
|
||||
css_class = "column form-column main col col-sm-9 form-horizontal"
|
||||
|
||||
|
||||
class Side(layout.Column):
|
||||
css_class = "column form-column sidebar col col-sm-3"
|
||||
|
||||
|
||||
class Container(layout.Div):
|
||||
css_class = "form-container row clearfix"
|
||||
|
||||
|
||||
# Override bootstrap3
|
||||
class InputGroup(layout.Field):
|
||||
|
||||
template = "xadmin/layout/input_group.html"
|
||||
|
||||
def __init__(self, field, *args, **kwargs):
|
||||
self.field = field
|
||||
self.inputs = list(args)
|
||||
if '@@' not in args:
|
||||
self.inputs.append('@@')
|
||||
|
||||
self.input_size = None
|
||||
css_class = kwargs.get('css_class', '')
|
||||
if 'input-lg' in css_class:
|
||||
self.input_size = 'input-lg'
|
||||
if 'input-sm' in css_class:
|
||||
self.input_size = 'input-sm'
|
||||
|
||||
super(InputGroup, self).__init__(field, **kwargs)
|
||||
|
||||
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs):
|
||||
classes = form.fields[self.field].widget.attrs.get('class', '')
|
||||
extra_context = {
|
||||
'inputs': self.inputs,
|
||||
'input_size': self.input_size,
|
||||
'classes': classes.replace('form-control', '')
|
||||
}
|
||||
if hasattr(self, 'wrapper_class'):
|
||||
extra_context['wrapper_class'] = self.wrapper_class
|
||||
|
||||
return render_field(
|
||||
self.field, form, form_style, context, template=self.template,
|
||||
attrs=self.attrs, template_pack=template_pack, extra_context=extra_context, **kwargs)
|
||||
|
||||
|
||||
class PrependedText(InputGroup):
|
||||
|
||||
def __init__(self, field, text, **kwargs):
|
||||
super(PrependedText, self).__init__(field, text, '@@', **kwargs)
|
||||
|
||||
|
||||
class AppendedText(InputGroup):
|
||||
|
||||
def __init__(self, field, text, **kwargs):
|
||||
super(AppendedText, self).__init__(field, '@@', text, **kwargs)
|
||||
|
||||
|
||||
class PrependedAppendedText(InputGroup):
|
||||
|
||||
def __init__(self, field, prepended_text=None, appended_text=None, *args, **kwargs):
|
||||
super(PrependedAppendedText, self).__init__(
|
||||
field, prepended_text, '@@', appended_text, **kwargs)
|
||||
BIN
Binary file not shown.
+1454
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+72
@@ -0,0 +1,72 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
# Azd325 <tim.kleinschmidt@gmail.com>, 2013
|
||||
# Azd325 <tim.kleinschmidt@gmail.com>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: Azd325 <tim.kleinschmidt@gmail.com>\n"
|
||||
"Language-Team: German (Germany) (http://www.transifex.com/projects/p/xadmin/language/de_DE/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de_DE\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] "%(sel)s von %(cnt)s markiert"
|
||||
msgstr[1] "%(sel)s von %(cnt)s markiert"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr "Neues Element"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr "Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag Sonntag"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr "So Mo Di Mi Do Fr Sa So"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr "So Mo Di Mi Do Fr Sa So"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr "Januar Februar März April Mai Juni Juli August September Oktober November Dezember"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr "Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr "Heute"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr "%a %d %b %Y %T %Z"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr "vorm nachm"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr "vorm nachm"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr "%T"
|
||||
Executable
BIN
Binary file not shown.
Executable
+1411
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+69
@@ -0,0 +1,69 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November "
|
||||
"December"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr ""
|
||||
BIN
Binary file not shown.
+1556
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+76
@@ -0,0 +1,76 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
# byroncorrales <byroncorrales@gmail.com>, 2013
|
||||
# byroncorrales <byroncorrales@gmail.com>, 2013
|
||||
# sacrac <crocha09.09@gmail.com>, 2013
|
||||
# netoxico <me@netoxico.com>, 2013
|
||||
# netoxico <me@netoxico.com>, 2013
|
||||
# sacrac <crocha09.09@gmail.com>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: sacrac <crocha09.09@gmail.com>\n"
|
||||
"Language-Team: Spanish (Mexico) (http://www.transifex.com/projects/p/xadmin/language/es_MX/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es_MX\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] "%(sel)s de %(cnt)s seleccionado."
|
||||
msgstr[1] "%(sel)s de %(cnt)s seleccionado "
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr "Nuevo elemento"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr "Domingo Lunes Martes Miércoles Jueves Viernes Sábado Domingo"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr "Dom Lun Mar Mié Jue Vie Sáb Dom"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr "Do Lu Ma Mi Ju Vi Sá Do"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr "Enero Febrero Marzo Abril Mayo Junio Julio Agosto Septiembre Octubre Noviembre Diciembre"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr "Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr "Hoy"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr "%a %d %b %Y %T %Z"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr "AM PM"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr "am pm"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr "%T"
|
||||
Executable
BIN
Binary file not shown.
Executable
+1475
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+71
@@ -0,0 +1,71 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
# unaizalakain <unai@gisa-elkartea.org>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: unaizalakain <unai@gisa-elkartea.org>\n"
|
||||
"Language-Team: Basque (http://www.transifex.com/projects/p/xadmin/language/eu/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: eu\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] "%(cnt)stik %(sel)s aukeratua"
|
||||
msgstr[1] "%(cnt)stik %(sel)s aukeratuak"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr "Elementu Berria"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr "Igandea Astelehena Asteartea Asteazkena Osteguna Ostirala Larunbata Igandea"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr "Iga Atl Atr Atz Otg Otr Lar Iga"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr "Ig At Ar Az Og Or La Ig"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr "Urtarrila Otsaila Martxoa Apirila Maiatza Ekaina Uztaila Abuztua Iraila Urria Azaroa Abendua"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr "Urt Ots Mar Api Mai Eka Uzt Abu Ira Urr Aza Abe"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr "Gaur"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr "%a %d %b %Y %T %Z"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr "AM PM"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr "am pm"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr "%T"
|
||||
BIN
Binary file not shown.
+1403
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+69
@@ -0,0 +1,69 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: sshwsfc <sshwsfc@gmail.com>\n"
|
||||
"Language-Team: Indonesian (Indonesia) (http://www.transifex.com/projects/p/xadmin/language/id_ID/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: id_ID\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] ""
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr ""
|
||||
Executable
BIN
Binary file not shown.
Executable
+1403
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+69
@@ -0,0 +1,69 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: sshwsfc <sshwsfc@gmail.com>\n"
|
||||
"Language-Team: Japanese (http://www.transifex.com/projects/p/xadmin/language/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ja\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] ""
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr ""
|
||||
Executable
BIN
Binary file not shown.
Executable
+1418
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+71
@@ -0,0 +1,71 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: sshwsfc <sshwsfc@gmail.com>\n"
|
||||
"Language-Team: Lithuanian (http://www.transifex.com/projects/p/xadmin/language/lt/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: lt\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr ""
|
||||
BIN
Binary file not shown.
+1410
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+70
@@ -0,0 +1,70 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: sshwsfc <sshwsfc@gmail.com>\n"
|
||||
"Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/xadmin/language/nl_NL/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl_NL\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr ""
|
||||
Executable
BIN
Binary file not shown.
Executable
+1481
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+83
@@ -0,0 +1,83 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-xadmin\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-08-12 21:07+0200\n"
|
||||
"PO-Revision-Date: 2014-08-12 21:23+0100\n"
|
||||
"Last-Translator: Michał Szpadzik <mszpadzik@gmail.com>\n"
|
||||
"Language-Team: Polish translators <mszpadzik@gmail.com>\n"
|
||||
"Language: pl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: Poedit 1.5.4\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:11
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] "%(sel)s z %(cnt)s wybranych"
|
||||
msgstr[1] "%(sel)s z %(cnt)s wybranych"
|
||||
msgstr[2] "%(sel)s z %(cnt)s wybranych"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.quick-form.js:172
|
||||
msgid "Close"
|
||||
msgstr "Zamknij"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.quick-form.js:173
|
||||
msgid "Add"
|
||||
msgstr "Dodaj"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr "Nowy obiekt"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr "niedziela poniedziałek wtorek środa czwartek piątek sobota niedziela"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr "niedz. pon. wt. śr. czw. pt. sob. niedz."
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr "niedz. pn. wt. śr. czw. pt. sob. niedz."
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November "
|
||||
"December"
|
||||
msgstr ""
|
||||
"styczeń luty marzec kwiecień maj czerwiec lipiec sierpień wrzesień "
|
||||
"październik "
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr "sty. lut. marz. kwie. maj czerw. lip. sier. wrze. paź. list. grudz."
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr "Dzisiaj"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr "%a %d %b %Y %T %Z"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr "AM PM"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr "am pm"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr "%T"
|
||||
BIN
Binary file not shown.
+1479
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+71
@@ -0,0 +1,71 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
# korndorfer <codigo.aberto@dorfer.com.br>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: korndorfer <codigo.aberto@dorfer.com.br>\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/xadmin/language/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt_BR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] "%(sel)s de %(cnt)s selecionado"
|
||||
msgstr[1] "%(sel)s de %(cnt)s selecionados"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr "Novo Item"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr "Domingo Segunda Terça Quarta Quinta Sexta Sábado Domingo"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr "Dom Seg Ter Qua Qui Sex Sáb Dom"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr "Do Sg Te Qa Qi Sx Sa Do"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr "Janeiro Fevereiro Março Abril Maio Junho Julho Agosto Setembro Outubro Novembro Dezembro"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr "Jan Fev Mar Abr Mai Jun Jul Ago Set Out Nov Dez"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr "Hoje"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr "%a %d %b %Y %T %Z"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr "AM PM"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr "am pm"
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr "%T"
|
||||
BIN
Binary file not shown.
+1438
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
+71
@@ -0,0 +1,71 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xadmin-core\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-30 23:11+0800\n"
|
||||
"PO-Revision-Date: 2013-11-20 12:41+0000\n"
|
||||
"Last-Translator: sshwsfc <sshwsfc@gmail.com>\n"
|
||||
"Language-Team: Russian (Russia) (http://www.transifex.com/projects/p/xadmin/language/ru_RU/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru_RU\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.actions.js:20
|
||||
msgid "%(sel)s of %(cnt)s selected"
|
||||
msgid_plural "%(sel)s of %(cnt)s selected"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#: static/xadmin/js/xadmin.plugin.revision.js:25
|
||||
msgid "New Item"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:32
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:33
|
||||
msgid "Sun Mon Tue Wed Thu Fri Sat Sun"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:34
|
||||
msgid "Su Mo Tu We Th Fr Sa Su"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:35
|
||||
msgid ""
|
||||
"January February March April May June July August September October November"
|
||||
" December"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:36
|
||||
msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:37
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:38
|
||||
msgid "%a %d %b %Y %T %Z"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:39
|
||||
msgid "AM PM"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:40
|
||||
msgid "am pm"
|
||||
msgstr ""
|
||||
|
||||
#: static/xadmin/js/xadmin.widget.datetime.js:43
|
||||
msgid "%T"
|
||||
msgstr ""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user