diff options
author | gnowgi <nagarjun@gnowledge.org> | 2012-03-15 16:19:20 +0530 |
---|---|---|
committer | gnowgi <nagarjun@gnowledge.org> | 2012-03-15 16:19:20 +0530 |
commit | 7a4f561e851fdc7246d804c3abb6748b8a4199a6 (patch) | |
tree | d2afc3463fd49625a9be482012f5c3bfcf7c42b9 /gstudio/admin | |
download | gnowsys-7a4f561e851fdc7246d804c3abb6748b8a4199a6.tar.gz |
master trunk of gnowsys-studio
Diffstat (limited to 'gstudio/admin')
37 files changed, 2793 insertions, 0 deletions
diff --git a/gstudio/admin/__init__.py b/gstudio/admin/__init__.py new file mode 100644 index 0000000..030e056 --- /dev/null +++ b/gstudio/admin/__init__.py @@ -0,0 +1,118 @@ +"""Admin of Gstudio""" +from django.contrib import admin + +#Models import +from gstudio.models import Objecttype +from gstudio.models import Metatype +from gstudio.models import Relation +from gstudio.models import Relationtype +from gstudio.models import Attribute +from gstudio.models import Attributetype +from gstudio.models import Systemtype +from gstudio.models import Processtype +from gstudio.models import AttributeSpecification +from gstudio.models import RelationSpecification +from gstudio.models import NodeSpecification +from gstudio.models import Union +from gstudio.models import Complement +from gstudio.models import Intersection +from gstudio.models import Expression +from gstudio.models import AttributeCharField +from gstudio.models import AttributeTextField +from gstudio.models import AttributeIntegerField +from gstudio.models import AttributeCommaSeparatedIntegerField +from gstudio.models import AttributeBigIntegerField +from gstudio.models import AttributePositiveIntegerField +from gstudio.models import AttributeDecimalField +from gstudio.models import AttributeFloatField +from gstudio.models import AttributeBooleanField +from gstudio.models import AttributeNullBooleanField +from gstudio.models import AttributeDateField +from gstudio.models import AttributeDateTimeField +from gstudio.models import AttributeTimeField +from gstudio.models import AttributeEmailField +from gstudio.models import AttributeFileField +from gstudio.models import AttributeFilePathField +from gstudio.models import AttributeImageField +from gstudio.models import AttributeURLField +from gstudio.models import AttributeIPAddressField + +#Admin imports + +from gstudio.admin.objecttype import ObjecttypeAdmin +from gstudio.admin.metatype import MetatypeAdmin +from gstudio.admin.relationtype import RelationtypeAdmin +from gstudio.admin.relation import RelationAdmin +from gstudio.admin.attribute import AttributeAdmin +from gstudio.admin.attributetype import AttributetypeAdmin +from gstudio.admin.attributespecification import AttributeSpecificationAdmin +from gstudio.admin.relationspecification import RelationSpecificationAdmin +from gstudio.admin.nodespecification import NodeSpecificationAdmin +from gstudio.admin.union import UnionAdmin +from gstudio.admin.complement import ComplementAdmin +from gstudio.admin.intersection import IntersectionAdmin +from gstudio.admin.expression import ExpressionAdmin +from gstudio.admin.systemtype import SystemtypeAdmin +from gstudio.admin.processtype import ProcesstypeAdmin + +from gstudio.admin.attribute_charfield import AttributeCharFieldAdmin +from gstudio.admin.attribute_textfield import AttributeTextFieldAdmin +from gstudio.admin.attribute_integerfield import AttributeIntegerFieldAdmin +from gstudio.admin.attribute_commaseparatedintegerfield import AttributeCommaSeparatedIntegerFieldAdmin +from gstudio.admin.attribute_bigintegerfield import AttributeBigIntegerFieldAdmin +from gstudio.admin.attribute_positiveintegerfield import AttributePositiveIntegerFieldAdmin +from gstudio.admin.attribute_decimalfield import AttributeDecimalFieldAdmin +from gstudio.admin.attribute_floatfield import AttributeFloatFieldAdmin +from gstudio.admin.attribute_booleanfield import AttributeBooleanFieldAdmin +from gstudio.admin.attribute_nullbooleanfield import AttributeNullBooleanFieldAdmin +from gstudio.admin.attribute_datefield import AttributeDateFieldAdmin +from gstudio.admin.attribute_datetimefield import AttributeDateTimeFieldAdmin +from gstudio.admin.attribute_timefield import AttributeTimeFieldAdmin +from gstudio.admin.attribute_emailfield import AttributeEmailFieldAdmin +from gstudio.admin.attribute_filefield import AttributeFileFieldAdmin +from gstudio.admin.attribute_filepathfield import AttributeFilePathFieldAdmin +from gstudio.admin.attribute_imagefield import AttributeImageFieldAdmin +from gstudio.admin.attribute_urlfield import AttributeURLFieldAdmin +from gstudio.admin.attribute_ipaddressfield import AttributeIPAddressFieldAdmin + + + + + +admin.site.register(Objecttype, ObjecttypeAdmin) +admin.site.register(Metatype, MetatypeAdmin) +admin.site.register(Relationtype, RelationtypeAdmin) +admin.site.register(Relation, RelationAdmin) +admin.site.register(Attribute, AttributeAdmin) +admin.site.register(Attributetype, AttributetypeAdmin) + +admin.site.register(Systemtype, SystemtypeAdmin) +admin.site.register(Processtype, ProcesstypeAdmin) +admin.site.register(AttributeSpecification, AttributeSpecificationAdmin) +admin.site.register(RelationSpecification, RelationSpecificationAdmin) +admin.site.register(NodeSpecification, NodeSpecificationAdmin) +admin.site.register(Union, UnionAdmin) +admin.site.register(Complement, ComplementAdmin) +admin.site.register(Intersection, IntersectionAdmin) +admin.site.register(Expression, ExpressionAdmin) + +admin.site.register(AttributeCharField, AttributeCharFieldAdmin) +admin.site.register(AttributeTextField, AttributeTextFieldAdmin) +admin.site.register(AttributeIntegerField, AttributeIntegerFieldAdmin) +admin.site.register(AttributeCommaSeparatedIntegerField, AttributeCommaSeparatedIntegerFieldAdmin) +admin.site.register(AttributeBigIntegerField,AttributeBigIntegerFieldAdmin) +admin.site.register(AttributePositiveIntegerField, AttributePositiveIntegerFieldAdmin) +admin.site.register(AttributeDecimalField, AttributeDecimalFieldAdmin) +admin.site.register(AttributeFloatField, AttributeFloatFieldAdmin) +admin.site.register(AttributeBooleanField, AttributeBooleanFieldAdmin) +admin.site.register(AttributeNullBooleanField, AttributeNullBooleanFieldAdmin) +admin.site.register(AttributeDateField, AttributeDateFieldAdmin) +admin.site.register(AttributeDateTimeField, AttributeDateTimeFieldAdmin) +admin.site.register(AttributeTimeField,AttributeTimeFieldAdmin) +admin.site.register(AttributeEmailField, AttributeEmailFieldAdmin) +admin.site.register(AttributeFileField, AttributeFileFieldAdmin) +admin.site.register(AttributeFilePathField, AttributeFilePathFieldAdmin) +admin.site.register(AttributeImageField, AttributeImageFieldAdmin) +admin.site.register(AttributeURLField, AttributeURLFieldAdmin) +admin.site.register(AttributeIPAddressField, AttributeIPAddressFieldAdmin) + diff --git a/gstudio/admin/attribute.py b/gstudio/admin/attribute.py new file mode 100644 index 0000000..336509e --- /dev/null +++ b/gstudio/admin/attribute.py @@ -0,0 +1,20 @@ +"""MetatypeAdmin for Gstudio""" +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ +from django.utils.html import escape +from gstudio.admin.forms import AttributeAdminForm +from gstudio.models import * +import reversion + +class AttributeAdmin(reversion.VersionAdmin): + class Media: + js = ("gstudio/js/gstudiojs.js",) + + def save_model(self, request, attribute, form, change): + attribute.title = attribute.composed_attribution + attribute.save() + + + + diff --git a/gstudio/admin/attribute_bigintegerfield.py b/gstudio/admin/attribute_bigintegerfield.py new file mode 100644 index 0000000..53085fb --- /dev/null +++ b/gstudio/admin/attribute_bigintegerfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeBigIntegerFieldAdminForm +import reversion + +class AttributeBigIntegerFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_booleanfield.py b/gstudio/admin/attribute_booleanfield.py new file mode 100644 index 0000000..e6ce41f --- /dev/null +++ b/gstudio/admin/attribute_booleanfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeBooleanFieldAdminForm +import reversion + +class AttributeBooleanFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_charfield.py b/gstudio/admin/attribute_charfield.py new file mode 100644 index 0000000..138d89d --- /dev/null +++ b/gstudio/admin/attribute_charfield.py @@ -0,0 +1,10 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeCharFieldAdminForm +import reversion + +class AttributeCharFieldAdmin(reversion.VersionAdmin): + pass + diff --git a/gstudio/admin/attribute_commaseparatedintegerfield.py b/gstudio/admin/attribute_commaseparatedintegerfield.py new file mode 100644 index 0000000..4202f61 --- /dev/null +++ b/gstudio/admin/attribute_commaseparatedintegerfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeCommaSeparatedIntegerFieldAdminForm +import reversion + +class AttributeCommaSeparatedIntegerFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_datefield.py b/gstudio/admin/attribute_datefield.py new file mode 100644 index 0000000..9619547 --- /dev/null +++ b/gstudio/admin/attribute_datefield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeDateFieldAdminForm +import reversion + +class AttributeDateFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_datetimefield.py b/gstudio/admin/attribute_datetimefield.py new file mode 100644 index 0000000..bdbdfa5 --- /dev/null +++ b/gstudio/admin/attribute_datetimefield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeDateTimeFieldAdminForm +import reversion + +class AttributeDateTimeFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_decimalfield.py b/gstudio/admin/attribute_decimalfield.py new file mode 100644 index 0000000..168ad06 --- /dev/null +++ b/gstudio/admin/attribute_decimalfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeDecimalFieldAdminForm +import reversion + +class AttributeDecimalFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_emailfield.py b/gstudio/admin/attribute_emailfield.py new file mode 100644 index 0000000..8173f2c --- /dev/null +++ b/gstudio/admin/attribute_emailfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeEmailFieldAdminForm +import reversion + +class AttributeEmailFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_filefield.py b/gstudio/admin/attribute_filefield.py new file mode 100644 index 0000000..a2553c6 --- /dev/null +++ b/gstudio/admin/attribute_filefield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeFileFieldAdminForm +import reversion + +class AttributeFileFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_filepathfield.py b/gstudio/admin/attribute_filepathfield.py new file mode 100644 index 0000000..76d483d --- /dev/null +++ b/gstudio/admin/attribute_filepathfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeFilePathFieldAdminForm +import reversion + +class AttributeFilePathFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_floatfield.py b/gstudio/admin/attribute_floatfield.py new file mode 100644 index 0000000..0f97b7c --- /dev/null +++ b/gstudio/admin/attribute_floatfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeFloatFieldAdminForm +import reversion + +class AttributeFloatFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_imagefield.py b/gstudio/admin/attribute_imagefield.py new file mode 100644 index 0000000..80515a6 --- /dev/null +++ b/gstudio/admin/attribute_imagefield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeImageFieldAdminForm +import reversion + +class AttributeImageFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_integerfield.py b/gstudio/admin/attribute_integerfield.py new file mode 100644 index 0000000..a170c95 --- /dev/null +++ b/gstudio/admin/attribute_integerfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeIntegerFieldAdminForm +import reversion + +class AttributeIntegerFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_ipaddressfield.py b/gstudio/admin/attribute_ipaddressfield.py new file mode 100644 index 0000000..1fa9a1b --- /dev/null +++ b/gstudio/admin/attribute_ipaddressfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeIPAddressFieldAdminForm +import reversion + +class AttributeIPAddressFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_nullbooleanfield.py b/gstudio/admin/attribute_nullbooleanfield.py new file mode 100644 index 0000000..fba5208 --- /dev/null +++ b/gstudio/admin/attribute_nullbooleanfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeNullBooleanFieldAdminForm +import reversion + +class AttributeNullBooleanFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_positiveintegerfield.py b/gstudio/admin/attribute_positiveintegerfield.py new file mode 100644 index 0000000..1404449 --- /dev/null +++ b/gstudio/admin/attribute_positiveintegerfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributePositiveIntegerFieldAdminForm +import reversion + +class AttributePositiveIntegerFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_textfield.py b/gstudio/admin/attribute_textfield.py new file mode 100644 index 0000000..7786af1 --- /dev/null +++ b/gstudio/admin/attribute_textfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeTextFieldAdminForm +import reversion + +class AttributeTextFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_timefield.py b/gstudio/admin/attribute_timefield.py new file mode 100644 index 0000000..929b028 --- /dev/null +++ b/gstudio/admin/attribute_timefield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeTimeFieldAdminForm +import reversion + +class AttributeTimeFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attribute_urlfield.py b/gstudio/admin/attribute_urlfield.py new file mode 100644 index 0000000..952beec --- /dev/null +++ b/gstudio/admin/attribute_urlfield.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeURLFieldAdminForm +import reversion + +class AttributeURLFieldAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attributespecification.py b/gstudio/admin/attributespecification.py new file mode 100644 index 0000000..3e7f6e1 --- /dev/null +++ b/gstudio/admin/attributespecification.py @@ -0,0 +1,10 @@ + +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import AttributeSpecificationAdminForm +import reversion + +class AttributeSpecificationAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/attributetype.py b/gstudio/admin/attributetype.py new file mode 100644 index 0000000..c1f03d9 --- /dev/null +++ b/gstudio/admin/attributetype.py @@ -0,0 +1,352 @@ +"""AttributetypeAdmin for Gstudio""" +from datetime import datetime + +from django.forms import Media +from django.contrib import admin +from django.contrib.auth.models import User +from django.utils.html import strip_tags +from django.utils.text import truncate_words +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns +from django.conf import settings as project_settings +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, NoReverseMatch + +from tagging.models import Tag + +import reversion +from gstudio import settings +from gstudio.managers import HIDDEN +from gstudio.managers import PUBLISHED +from gstudio.ping import DirectoryPinger +from gstudio.admin.forms import AttributetypeAdminForm + + + + +class AttributetypeAdmin(reversion.VersionAdmin): + """Admin for Attributetype model""" + form = AttributetypeAdminForm + date_hierarchy = 'creation_date' + fieldsets = ((_('Attribute Definiton'), {'fields': ('title','altnames','subjecttype','applicable_node\ +types','dataType','verbose_name','null','blank','help_text','max_digits','decimal_places','auto_now','auto_now_add','upload_to','path','verify_exists','parent','slug','status') }), + + (_('Content'), {'fields': ('content', 'image',), + 'classes': ('collapse', 'collapse-closed')}), + + + (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',), + + 'classes': ('collapse', 'collapse-closed')}), + (_('Options'), {'fields': ('featured', 'excerpt', 'template', + 'authors', + 'creation_date', + 'start_publication', + 'end_publication'), + 'classes': ('collapse', 'collapse-closed')}), + (_('Privacy'), {'fields': ('password', 'login_required',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Publication'), {'fields': ('tags', + 'sites')})) + + + list_filter = ('parent','metatypes', 'authors', 'status', 'featured', + 'login_required', 'comment_enabled', 'pingback_enabled', + 'creation_date', 'start_publication', + 'end_publication', 'sites') + list_display = ('get_title', 'get_authors', 'get_metatypes', + 'get_tags', 'get_sites', + 'get_comments_are_open', 'pingback_enabled', + 'get_is_actual', 'get_is_visible', 'get_link', + 'get_short_url', 'creation_date') + radio_fields = {'template': admin.VERTICAL} + filter_horizontal = ('metatypes', 'authors') + prepopulated_fields = {'slug': ('title', )} + search_fields = ('title', 'excerpt', 'content', 'tags') + actions = ['make_mine', 'make_published', 'make_hidden', + 'close_comments', 'close_pingbacks', + 'ping_directories', 'make_tweet', 'put_on_top'] + actions_on_top = True + actions_on_bottom = True + + def __init__(self, model, admin_site): + self.form.admin_site = admin_site + super(AttributetypeAdmin, self).__init__(model, admin_site) + + # Custom Display + def get_title(self, attributetype): + """Return the title with word count and number of comments""" + title = _('%(title)s (%(word_count)i words)') % \ + {'title': attributetype.title, 'word_count': attributetype.word_count} + comments = attributetype.comments.count() + if comments: + return _('%(title)s (%(comments)i comments)') % \ + {'title': title, 'comments': comments} + return title + get_title.short_description = _('title') + + def get_authors(self, attributetype): + """Return the authors in HTML""" + try: + authors = ['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_author_detail', + args=[author.username]), + author.username) for author in attributetype.authors.all()] + except NoReverseMatch: + authors = [author.username for author in attributetype.authors.all()] + return ', '.join(authors) + get_authors.allow_tags = True + get_authors.short_description = _('author(s)') + + def get_metatypes(self, attributetype): + """Return the metatypes linked in HTML""" + try: + metatypes = ['<a href="%s" target="blank">%s</a>' % + (metatype.get_absolute_url(), metatype.title) + for metatype in attributetype.metatypes.all()] + except NoReverseMatch: + metatypes = [metatype.title for metatype in + attributetype.metatypes.all()] + return ', '.join(metatypes) + get_metatypes.allow_tags = True + get_metatypes.short_description = _('metatype(s)') + + def get_tags(self, attributetype): + """Return the tags linked in HTML""" + try: + return ', '.join(['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_tag_detail', + args=[tag.name]), tag.name) + for tag in Tag.attributes.get_for_attribute(attributetype)]) + except NoReverseMatch: + return attributetype.tags + get_tags.allow_tags = True + get_tags.short_description = _('tag(s)') + + def get_sites(self, attributetype): + """Return the sites linked in HTML""" + return ', '.join( + ['<a href="http://%(domain)s" target="blank">%(name)s</a>' % + site.__dict__ for site in attributetype.sites.all()]) + get_sites.allow_tags = True + get_sites.short_description = _('site(s)') + + def get_comments_are_open(self, attributetype): + """Admin wrapper for attributetype.comments_are_open""" + return attributetype.comments_are_open + get_comments_are_open.boolean = True + get_comments_are_open.short_description = _('comment enabled') + + def get_is_actual(self, attributetype): + """Admin wrapper for attributetype.is_actual""" + return attributetype.is_actual + get_is_actual.boolean = True + get_is_actual.short_description = _('is actual') + + def get_is_visible(self, attributetype): + """Admin wrapper for attributetype.is_visible""" + return attributetype.is_visible + get_is_visible.boolean = True + get_is_visible.short_description = _('is visible') + + def get_link(self, attributetype): + """Return a formated link to the attributetype""" + return u'<a href="%s" target="blank">%s</a>' % ( + attributetype.get_absolute_url(), _('View')) + get_link.allow_tags = True + get_link.short_description = _('View on site') + + def get_short_url(self, attributetype): + """Return the short url in HTML""" + short_url = attributetype.short_url + if not short_url: + return _('Unavailable') + return '<a href="%(url)s" target="blank">%(url)s</a>' % \ + {'url': short_url} + get_short_url.allow_tags = True + get_short_url.short_description = _('short url') + + # Custom Methods + def save_model(self, request, attributetype, form, change): + """Save the authors, update time, make an excerpt""" + if not form.cleaned_data.get('excerpt') and attributetype.status == PUBLISHED: + attributetype.excerpt = truncate_words(strip_tags(attributetype.content), 50) + + if attributetype.pk and not request.user.has_perm('gstudio.can_change_author'): + form.cleaned_data['authors'] = attributetype.authors.all() + + if not form.cleaned_data.get('authors'): + form.cleaned_data['authors'].append(request.user) + + attributetype.last_update = datetime.now() + attributetype.save() + + def queryset(self, request): + """Make special filtering by user permissions""" + queryset = super(AttributetypeAdmin, self).queryset(request) + if request.user.has_perm('gstudio.can_view_all'): + return queryset + return request.user.attributetypes.all() + + def formfield_for_manytomany(self, db_field, request, **kwargs): + """Filters the disposable authors""" + if db_field.name == 'authors': + if request.user.has_perm('gstudio.can_change_author'): + kwargs['queryset'] = User.objects.filter(is_staff=True) + else: + kwargs['queryset'] = User.objects.filter(pk=request.user.pk) + + return super(AttributetypeAdmin, self).formfield_for_manytomany( + db_field, request, **kwargs) + + def get_actions(self, request): + """Define user actions by permissions""" + actions = super(AttributetypeAdmin, self).get_actions(request) + if not request.user.has_perm('gstudio.can_change_author') \ + or not request.user.has_perm('gstudio.can_view_all'): + del actions['make_mine'] + if not settings.PING_DIRECTORIES: + del actions['ping_directories'] + if not settings.USE_TWITTER: + del actions['make_tweet'] + + return actions + + # Custom Actions + def make_mine(self, request, queryset): + """Set the attributetypes to the user""" + for attributetype in queryset: + if request.user not in attributetype.authors.all(): + attributetype.authors.add(request.user) + self.message_user( + request, _('The selected attributetypes now belong to you.')) + make_mine.short_description = _('Set the attributetypes to the user') + + def make_published(self, request, queryset): + """Set attributetypes selected as published""" + queryset.update(status=PUBLISHED) + self.ping_directories(request, queryset, messages=False) + self.message_user( + request, _('The selected attributetypes are now marked as published.')) + make_published.short_description = _('Set attributetypes selected as published') + + def make_hidden(self, request, queryset): + """Set attributetypes selected as hidden""" + queryset.update(status=HIDDEN) + self.message_user( + request, _('The selected attributetypes are now marked as hidden.')) + make_hidden.short_description = _('Set attributetypes selected as hidden') + + def make_tweet(self, request, queryset): + """Post an update on Twitter""" + import tweepy + auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY, + settings.TWITTER_CONSUMER_SECRET) + auth.set_access_token(settings.TWITTER_ACCESS_KEY, + settings.TWITTER_ACCESS_SECRET) + api = tweepy.API(auth) + for attributetype in queryset: + short_url = attributetype.short_url + message = '%s %s' % (attributetype.title[:139 - len(short_url)], short_url) + api.update_status(message) + self.message_user( + request, _('The selected attributetypes have been tweeted.')) + make_tweet.short_description = _('Tweet attributetypes selected') + + def close_comments(self, request, queryset): + """Close the comments for selected attributetypes""" + queryset.update(comment_enabled=False) + self.message_user( + request, _('Comments are now closed for selected attributetypes.')) + close_comments.short_description = _('Close the comments for '\ + 'selected attributetypes') + + def close_pingbacks(self, request, queryset): + """Close the pingbacks for selected attributetypes""" + queryset.update(pingback_enabled=False) + self.message_user( + request, _('Linkbacks are now closed for selected attributetypes.')) + close_pingbacks.short_description = _( + 'Close the linkbacks for selected attributetypes') + + def put_on_top(self, request, queryset): + """Put the selected attributetypes on top at the current date""" + queryset.update(creation_date=datetime.now()) + self.ping_directories(request, queryset, messages=False) + self.message_user(request, _( + 'The selected attributetypes are now set at the current date.')) + put_on_top.short_description = _( + 'Put the selected attributetypes on top at the current date') + + def ping_directories(self, request, queryset, messages=True): + """Ping Directories for selected attributetypes""" + for directory in settings.PING_DIRECTORIES: + pinger = DirectoryPinger(directory, queryset) + pinger.join() + if messages: + success = 0 + for result in pinger.results: + if not result.get('flerror', True): + success += 1 + else: + self.message_user(request, + '%s : %s' % (directory, + result['message'])) + if success: + self.message_user( + request, + _('%(directory)s directory succesfully ' \ + 'pinged %(success)d attributetypes.') % + {'directory': directory, 'success': success}) + ping_directories.short_description = _( + 'Ping Directories for selected attributetypes') + + def get_urls(self): + attributetype_admin_urls = super(AttributetypeAdmin, self).get_urls() + urls = patterns( + 'django.views.generic.simple', + url(r'^autocomplete_tags/$', 'direct_to_template', + {'template': 'admin/gstudio/attributetype/autocomplete_tags.js', + 'mimetype': 'application/javascript'}, + name='gstudio_attributetype_autocomplete_tags'), + url(r'^wymeditor/$', 'direct_to_template', + {'template': 'admin/gstudio/attributetype/wymeditor.js', + 'mimetype': 'application/javascript'}, + name='gstudio_attributetype_wymeditor'), + url(r'^markitup/$', 'direct_to_template', + {'template': 'admin/gstudio/attributetype/markitup.js', + 'mimetype': 'application/javascript'}, + name='gstudio_attributetype_markitup'),) + return urls + attributetype_admin_urls + + def _media(self): + STATIC_URL = '%sgstudio/' % project_settings.STATIC_URL + media = super(AttributetypeAdmin, self).media + Media( + css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)}, + js=('%sjs/jquery.js' % STATIC_URL, + '%sjs/jquery.bgiframe.js' % STATIC_URL, + '%sjs/jquery.autocomplete.js' % STATIC_URL, + reverse('admin:gstudio_attributetype_autocomplete_tags'),)) + + if settings.WYSIWYG == 'wymeditor': + media += Media( + js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL, + '%sjs/wymeditor/plugins/hovertools/' + 'jquery.wymeditor.hovertools.js' % STATIC_URL, + reverse('admin:gstudio_attributetype_wymeditor'))) + elif settings.WYSIWYG == 'tinymce': + from tinymce.widgets import TinyMCE + media += TinyMCE().media + Media( + js=(reverse('tinymce-js', args=('admin/gstudio/attributetype',)),)) + elif settings.WYSIWYG == 'markitup': + media += Media( + js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL, + '%sjs/markitup/sets/%s/set.js' % ( + STATIC_URL, settings.MARKUP_LANGUAGE), + reverse('admin:gstudio_attributetype_markitup')), + css={'all': ( + '%sjs/markitup/skins/django/style.css' % STATIC_URL, + '%sjs/markitup/sets/%s/style.css' % ( + STATIC_URL, settings.MARKUP_LANGUAGE))}) + return media + media = property(_media) diff --git a/gstudio/admin/complement.py b/gstudio/admin/complement.py new file mode 100644 index 0000000..b35ad6c --- /dev/null +++ b/gstudio/admin/complement.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import ComplementAdminForm +import reversion + +class ComplementAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/expression.py b/gstudio/admin/expression.py new file mode 100644 index 0000000..a6a8fd2 --- /dev/null +++ b/gstudio/admin/expression.py @@ -0,0 +1,13 @@ +"""MetatypeAdmin for Gstudio""" +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import ExpressionAdminForm +import reversion + +class ExpressionAdmin(reversion.VersionAdmin): + def save_model(self, request, expression, form, change): + expression.title = expression.composed_sentence + expression.save() + diff --git a/gstudio/admin/forms.py b/gstudio/admin/forms.py new file mode 100644 index 0000000..37b578c --- /dev/null +++ b/gstudio/admin/forms.py @@ -0,0 +1,505 @@ +"""Forms for Gstudio admin""" +from django import forms +from django.db.models import ManyToOneRel +from django.db.models import ManyToManyRel +from django.contrib.sites.models import Site +from django.utils.translation import ugettext_lazy as _ +from django.contrib.admin.widgets import RelatedFieldWidgetWrapper + +from gstudio.models import NID +from gstudio.models import Nodetype +from gstudio.models import Objecttype +from gstudio.models import Metatype +from gstudio.models import Relationtype +from gstudio.models import Relation +from gstudio.models import Attributetype +from gstudio.models import Attribute +from gstudio.models import AttributeSpecification +from gstudio.models import RelationSpecification +from gstudio.models import NodeSpecification +from gstudio.models import Union +from gstudio.models import Complement +from gstudio.models import Intersection +from gstudio.models import Expression + +from gstudio.models import AttributeCharField +from gstudio.models import AttributeTextField +from gstudio.models import AttributeIntegerField +from gstudio.models import AttributeCommaSeparatedIntegerField +from gstudio.models import AttributeBigIntegerField +from gstudio.models import AttributePositiveIntegerField +from gstudio.models import AttributeDecimalField +from gstudio.models import AttributeFloatField +from gstudio.models import AttributeBooleanField +from gstudio.models import AttributeNullBooleanField +from gstudio.models import AttributeDateField +from gstudio.models import AttributeDateTimeField +from gstudio.models import AttributeTimeField +from gstudio.models import AttributeEmailField +from gstudio.models import AttributeFileField +from gstudio.models import AttributeFilePathField +from gstudio.models import AttributeImageField +from gstudio.models import AttributeURLField +from gstudio.models import AttributeIPAddressField + + + + +from gstudio.models import Systemtype +from gstudio.models import Processtype + + +from gstudio.admin.widgets import TreeNodeChoiceField +from gstudio.admin.widgets import MPTTFilteredSelectMultiple +from gstudio.admin.widgets import MPTTModelMultipleChoiceField +from reversion.models import Version + +class MetatypeAdminForm(forms.ModelForm): + """Form for Metatype's Admin""" + parent = TreeNodeChoiceField( + label=_('parent metatype').capitalize(), + required=False, empty_label=_('No parent metatype'), + queryset=Metatype.tree.all()) + + def __init__(self, *args, **kwargs): + super(MetatypeAdminForm, self).__init__(*args, **kwargs) + rel = ManyToOneRel(Metatype, 'id') + self.fields['parent'].widget = RelatedFieldWidgetWrapper( + self.fields['parent'].widget, rel, self.admin_site) + + def clean_parent(self): + """Check if metatype parent is not selfish""" + data = self.cleaned_data['parent'] + if data == self.instance: + raise forms.ValidationError( + _('A metatype cannot be a parent of itself.')) + return data + + class Meta: + """MetatypeAdminForm's Meta""" + model = Metatype + + +class ObjecttypeAdminForm(forms.ModelForm): + """Form for Objecttype's Admin""" + + parent = TreeNodeChoiceField( + label=_('parent nodetype').capitalize(), + required=False, empty_label=_('No parent nodetype'), + queryset=Nodetype.tree.all()) + + metatypes = MPTTModelMultipleChoiceField( + label=_('Metatypes'), required=False, + queryset=Metatype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('metatypes'), False, + attrs={'rows': '10'})) + priornodes = MPTTModelMultipleChoiceField( + label=_('priornodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('nodetypes'), False, + attrs={'rows': '10'})) + + posteriornodes = MPTTModelMultipleChoiceField( + label=_('posteriornodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('nodetypes'), False, + attrs={'rows': '10'})) + + + + + def __init__(self, *args, **kwargs): + super(ObjecttypeAdminForm, self).__init__(*args, **kwargs) + meta = ManyToManyRel(Metatype, 'id') + prior = ManyToManyRel(Nodetype,'id') + post = ManyToManyRel(Nodetype,'id') + self.fields['metatypes'].widget = RelatedFieldWidgetWrapper( + self.fields['metatypes'].widget, meta, self.admin_site) + self.fields['priornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['priornodes'].widget, prior, self.admin_site) + self.fields['posteriornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['posteriornodes'].widget, post, self.admin_site) + + + + self.fields['sites'].initial = [Site.objects.get_current()] + + def clean_parent(self): + """Check if an object does not become a parent of itself""" + data = self.cleaned_data['parent'] + if data == self.instance: + raise forms.ValidationError( + _('An objectype cannot be parent of itself.')) + return data + + class Meta: + """NodetypeAdminForm's Meta""" + model = Objecttype + + +class RelationtypeAdminForm(forms.ModelForm): + + priornodes = MPTTModelMultipleChoiceField( + label=_('Priornodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('priornodes'), False, + attrs={'rows': '10'})) + posteriornodes = MPTTModelMultipleChoiceField( + label=_('Prosterior Nodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('prosteriornode'), False, + attrs={'rows': '10'})) + + def __init__(self, *args, **kwargs): + super(RelationtypeAdminForm, self).__init__(*args, **kwargs) + prior = ManyToManyRel(Nodetype, 'id') + post = ManyToManyRel(Nodetype, 'id') + + + + self.fields['priornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['priornodes'].widget, prior, self.admin_site) + self.fields['posteriornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['posteriornodes'].widget, post, self.admin_site) + + + + + class Meta: + """MetatypeAdminForm's Meta""" + model = Relationtype + + +class RelationAdminForm(forms.ModelForm): + + + def ApplicableNodeTypes_filter(self): + choice = 'NT' + if choice == 'ED': + nid = 'Edge' + if choice == 'ND': + nid = 'Node' + if choice == 'NT': + nid = 'Nodetype' + if choice == 'ET': + nid = 'Edgetype' + if choice == 'OT': + nid = 'Objecttype' + if choice == 'RT': + nid = 'Relationtype' + if choice == 'MT': + nid = 'Metatype' + if choice == 'AT': + nid = 'Attributetype' + if choice == 'RN': + nid = 'Relation' + if choice == 'AS': + nid = 'Attributes' + if choice == 'ST': + nid = 'Systemtype' + if choice == 'SY': + nid = 'System' + + node = NID.objects.get(Objecttype) + vrs = Version.objects.filter(type=0 , object_id=node.id) + vrs = vrs[0] + AppNode = vrs.object._meta.module_name + AppNodeList = AppNode.objects.all() + return AppNodeList + + + + + + + + + + + + + + + + class Meta: + """MetatypeAdminForm's Meta""" + model = Relation + + +class ProcesstypeAdminForm(forms.ModelForm): + + priornodes = MPTTModelMultipleChoiceField( + label=_('Priornodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('priornodes'), False, + attrs={'rows': '10'})) + posteriornodes = MPTTModelMultipleChoiceField( + label=_('Prosterior Nodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('prosteriornode'), False, + attrs={'rows': '10'})) + attributetype_set = MPTTModelMultipleChoiceField( + label=_('Attributetype Sets'), required=False, + queryset=Attributetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('Attributetype Set'), False, + attrs={'rows': '10'})) + relationtype_set = MPTTModelMultipleChoiceField( + label=_('Relationtype Set'), required=False, + queryset=Relationtype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('Relationtype Set'), False, + attrs={'rows': '10'})) + + + def __init__(self, *args, **kwargs): + super(ProcesstypeAdminForm, self).__init__(*args, **kwargs) + prior = ManyToManyRel(Nodetype, 'id') + post = ManyToManyRel(Nodetype, 'id') + atype = ManyToManyRel(Attributetype, 'id') + rtype = ManyToManyRel(Relationtype, 'id') + + + self.fields['priornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['priornodes'].widget, prior, self.admin_site) + self.fields['posteriornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['posteriornodes'].widget, post, self.admin_site) + self.fields['attributetype_set'].widget = RelatedFieldWidgetWrapper( + self.fields['attributetype_set'].widget, atype, self.admin_site) + self.fields['relationtype_set'].widget = RelatedFieldWidgetWrapper( + self.fields['relationtype_set'].widget, rtype, self.admin_site) + + + + + class Meta: + """SystemAdminForm's Meta""" + model = Processtype + +class AttributetypeAdminForm(forms.ModelForm): + priornodes = MPTTModelMultipleChoiceField( + label=_('Priornodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('priornodes'), False, + attrs={'rows': '10'})) + posteriornodes = MPTTModelMultipleChoiceField( + label=_('Posterior Nodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('posteriornodes'), False, + attrs={'rows': '10'})) + def __init__(self, *args, **kwargs): + super(AttributetypeAdminForm, self).__init__(*args, **kwargs) + prior = ManyToManyRel(Nodetype, 'id') + post = ManyToManyRel(Nodetype, 'id') + + + self.fields['priornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['priornodes'].widget, prior, self.admin_site) + self.fields['posteriornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['posteriornodes'].widget, post, self.admin_site) + + + + class Meta: + """MetatypeAdminForm's Meta""" + model = Attributetype + + +class AttributeAdminForm(forms.ModelForm): + + def subject_filter(attr): + """ + returns applicable selection of nodes for selecting objects + """ + for each in Objecttype.objects.all(): + if attr.subjecttype.id == each.id: + return each.get_members + + def __init__(self, *args, **kwargs): + super(AttributeAdminForm, self).__init__(*args, **kwargs) + self.fields["subject"].queryset = subject_filter(attr) + + + class Meta: + """MetatypeAdminForm's Meta""" + model = Attribute + + + +class SystemtypeAdminForm(forms.ModelForm): + nodetype_set = MPTTModelMultipleChoiceField( + label=_('Nodetypeset'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('Nodetypesets'), False, + attrs={'rows': '10'})) + relationtype_set = MPTTModelMultipleChoiceField( + label=_('Relationtypeset'), required=False, + queryset=Relationtype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('Relationtypesets'), False, + attrs={'rows': '10'})) + attributetype_set = MPTTModelMultipleChoiceField( + label=_('Attributetypeset'), required=False, + queryset=Attributetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('Attributetypesets'), False, + attrs={'rows': '10'})) + metatype_set = MPTTModelMultipleChoiceField( + label=_('Metatypeset'), required=False, + queryset=Metatype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('metatypesets'), False, + attrs={'rows': '10'})) + processtype_set = MPTTModelMultipleChoiceField( + label=_('Processtypeset'), required=False, + queryset=Processtype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('Processtypesets'), False, + attrs={'rows': '10'})) + + priornodes = MPTTModelMultipleChoiceField( + label=_('priornodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('nodetypes'), False, + attrs={'rows': '10'})) + + posteriornodes = MPTTModelMultipleChoiceField( + label=_('posteriornodes'), required=False, + queryset=Nodetype.objects.all(), + widget=MPTTFilteredSelectMultiple(_('nodetypes'), False, + attrs={'rows': '10'})) + def __init__(self, *args, **kwargs): + super(SystemtypeAdminForm, self).__init__(*args, **kwargs) + ot = ManyToManyRel(Nodetype,'id') + rt = ManyToManyRel(Relationtype,'id') + at = ManyToManyRel(Attributetype,'id') + mt = ManyToManyRel(Metatype,'id') + pt = ManyToManyRel(Processtype,'id') + prior = ManyToManyRel(Nodetype,'id') + post = ManyToManyRel(Nodetype,'id') + + self.fields['nodetype_set'].widget = RelatedFieldWidgetWrapper( + self.fields['nodetype_set'].widget, ot, self.admin_site) + self.fields['relationtype_set'].widget = RelatedFieldWidgetWrapper( + self.fields['relationtype_set'].widget, rt, self.admin_site) + self.fields['attributetype_set'].widget = RelatedFieldWidgetWrapper( + self.fields['attributetype_set'].widget, at, self.admin_site) + self.fields['metatype_set'].widget = RelatedFieldWidgetWrapper( + self.fields['metatype_set'].widget, mt, self.admin_site) + self.fields['processtype_set'].widget = RelatedFieldWidgetWrapper( + self.fields['processtype_set'].widget, pt, self.admin_site) + self.fields['priornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['priornodes'].widget, prior, self.admin_site) + self.fields['posteriornodes'].widget = RelatedFieldWidgetWrapper( + self.fields['posteriornodes'].widget, post, self.admin_site) + + + + + class Meta: + """SystemAdminForm's Meta""" + model = Systemtype + + +class AttributeSpecificationAdminForm(forms.ModelForm): + class Meta: + model = AttributeSpecification + +class RelationSpecificationAdminForm(forms.ModelForm): + class Meta: + model = RelationSpecification + +class NodeSpecificationAdminForm(forms.ModelForm): + class Meta: + model = NodeSpecification + +class UnionAdminForm(forms.ModelForm): + class Meta: + model = Union + +class ComplementAdminForm(forms.ModelForm): + class Meta: + model = Complement + + + +class IntersectionAdminForm(forms.ModelForm): + class Meta: + model = Intersection + + +class ExpressionAdminForm(forms.ModelForm): + class Meta: + model = Expression + +### Datatypes here ### + +class AttributeCharFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeCharField + +class AttributeTextFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeTextField + +class AttributeIntegerFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeIntegerField + +class AttributeCommaSeparatedIntegerFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeCommaSeparatedIntegerField +class AttributeBigIntegerFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeBigIntegerField +class AttributePositiveIntegerFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributePositiveIntegerField + +class AttributeDecimalFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeDecimalField +class AttributeFloatFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeFloatField +class AttributeBooleanFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeBooleanField + +class AttributeNullBooleanFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeNullBooleanField +class AttributeDateFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeDateField +class AttributeDateTimeFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeDateField + +class AttributeTimeFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeTimeField + +class AttributeEmailFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeEmailField +class AttributeFileFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeFileField +class AttributeFilePathFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeFilePathField +class AttributeImageFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeImageField + +class AttributeURLFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeURLField +class AttributeIPAddressFieldAdminForm(forms.ModelForm): + class Meta: + model = AttributeIPAddressField + + + + + + + + + + diff --git a/gstudio/admin/intersection.py b/gstudio/admin/intersection.py new file mode 100644 index 0000000..6e9dd3e --- /dev/null +++ b/gstudio/admin/intersection.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import IntersectionAdminForm +import reversion + +class IntersectionAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/metatype.py b/gstudio/admin/metatype.py new file mode 100644 index 0000000..bb24fc7 --- /dev/null +++ b/gstudio/admin/metatype.py @@ -0,0 +1,30 @@ +"""MetatypeAdmin for Gstudio""" +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import MetatypeAdminForm +import reversion + +class MetatypeAdmin(reversion.VersionAdmin): + """Admin for Metatype model""" + form = MetatypeAdminForm + fields = ('title','altnames', 'parent', 'description', 'slug') + list_display = ('title', 'slug', 'get_tree_path', 'description') + prepopulated_fields = {'slug': ('title', )} + search_fields = ('title', 'description') + list_filter = ('parent',) + + def __init__(self, model, admin_site): + self.form.admin_site = admin_site + super(MetatypeAdmin, self).__init__(model, admin_site) + + def get_tree_path(self, metatype): + """Return the metatype's tree path in HTML""" + try: + return '<a href="%s" target="blank">/%s/</a>' % \ + (metatype.get_absolute_url(), metatype.tree_path) + except NoReverseMatch: + return '/%s/' % metatype.tree_path + get_tree_path.allow_tags = True + get_tree_path.short_description = _('tree path') diff --git a/gstudio/admin/nodespecification.py b/gstudio/admin/nodespecification.py new file mode 100644 index 0000000..aaf784e --- /dev/null +++ b/gstudio/admin/nodespecification.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import NodeSpecificationAdminForm +import reversion + +class NodeSpecificationAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/objecttype.py b/gstudio/admin/objecttype.py new file mode 100644 index 0000000..22ef205 --- /dev/null +++ b/gstudio/admin/objecttype.py @@ -0,0 +1,345 @@ +"""ObjecttypeAdmin for Gstudio""" +from datetime import datetime +from django.forms import Media +from django.contrib import admin +from django.contrib.auth.models import User +from django.utils.html import strip_tags +from django.utils.text import truncate_words +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns +from django.conf import settings as project_settings +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, NoReverseMatch + +from tagging.models import Tag + +import reversion +from gstudio import settings +from gstudio.managers import HIDDEN +from gstudio.managers import PUBLISHED +from gstudio.ping import DirectoryPinger +from gstudio.admin.forms import ObjecttypeAdminForm + + + + +class ObjecttypeAdmin(reversion.VersionAdmin): + """Admin for Objecttype model""" + form = ObjecttypeAdminForm + date_hierarchy = 'creation_date' + fieldsets = ((_('Neighbourhood'), {'fields': ('title','altnames','plural','parent','slug', + 'metatypes','tags','image', 'status','content')}), + (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Options'), {'fields': ('featured', 'excerpt', 'template', + 'authors', + 'creation_date', + 'start_publication', + 'end_publication'), + 'classes': ('collapse', 'collapse-closed')}), + (_('Privacy'), {'fields': ('password', 'login_required',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Discussion'), {'fields': ('comment_enabled', + 'pingback_enabled','sites'),'classes': ('collapse', 'collapse-closed')}), + + ) + list_filter = ('parent','metatypes', 'authors', 'status', 'featured', + 'login_required', 'comment_enabled', 'pingback_enabled', + 'creation_date', 'start_publication', + 'end_publication', 'sites') + list_display = ('get_title', 'get_authors', 'get_metatypes', + 'get_tags', 'get_sites', + 'get_comments_are_open', 'pingback_enabled', + 'get_is_actual', 'get_is_visible', 'get_link', + 'get_short_url', 'creation_date') + radio_fields = {'template': admin.VERTICAL} + filter_horizontal = ('metatypes', 'authors' ) + prepopulated_fields = {'slug': ('title', )} + search_fields = ('title', 'excerpt', 'content', 'tags') + actions = ['make_mine', 'make_published', 'make_hidden', + 'close_comments', 'close_pingbacks', + 'ping_directories', 'make_tweet', 'put_on_top'] + actions_on_top = True + actions_on_bottom = True + + def __init__(self, model, admin_site): + self.form.admin_site = admin_site + super(ObjecttypeAdmin, self).__init__(model, admin_site) + + # Custom Display + def get_title(self, nodetype): + """Return the title with word count and number of comments""" + title = _('%(title)s (%(word_count)i words)') % \ + {'title': nodetype.title, 'word_count': nodetype.word_count} + comments = nodetype.comments.count() + if comments: + return _('%(title)s (%(comments)i comments)') % \ + {'title': title, 'comments': comments} + return title + get_title.short_description = _('title') + + def get_authors(self, nodetype): + """Return the authors in HTML""" + try: + authors = ['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_author_detail', + args=[author.username]), + author.username) for author in nodetype.authors.all()] + except NoReverseMatch: + authors = [author.username for author in nodetype.authors.all()] + return ', '.join(authors) + get_authors.allow_tags = True + get_authors.short_description = _('author(s)') + + def get_metatypes(self, nodetype): + """Return the metatypes linked in HTML""" + try: + metatypes = ['<a href="%s" target="blank">%s</a>' % + (metatype.get_absolute_url(), metatype.title) + for metatype in nodetype.metatypes.all()] + except NoReverseMatch: + metatypes = [metatype.title for metatype in + nodetype.metatypes.all()] + return ', '.join(metatypes) + get_metatypes.allow_tags = True + get_metatypes.short_description = _('metatype(s)') + + def get_tags(self, nodetype): + """Return the tags linked in HTML""" + try: + return ', '.join(['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_tag_detail', + args=[tag.name]), tag.name) + for tag in Tag.objects.get_for_object(nodetype)]) + except NoReverseMatch: + return nodetype.tags + get_tags.allow_tags = True + get_tags.short_description = _('tag(s)') + + def get_sites(self, nodetype): + """Return the sites linked in HTML""" + return ', '.join( + ['<a href="http://%(domain)s" target="blank">%(name)s</a>' % + site.__dict__ for site in nodetype.sites.all()]) + get_sites.allow_tags = True + get_sites.short_description = _('site(s)') + + def get_comments_are_open(self, nodetype): + """Admin wrapper for nodetype.comments_are_open""" + return nodetype.comments_are_open + get_comments_are_open.boolean = True + get_comments_are_open.short_description = _('comment enabled') + + def get_is_actual(self, nodetype): + """Admin wrapper for nodetype.is_actual""" + return nodetype.is_actual + get_is_actual.boolean = True + get_is_actual.short_description = _('is actual') + + def get_is_visible(self, nodetype): + """Admin wrapper for nodetype.is_visible""" + return nodetype.is_visible + get_is_visible.boolean = True + get_is_visible.short_description = _('is visible') + + def get_link(self, nodetype): + """Return a formated link to the nodetype""" + return u'<a href="%s" target="blank">%s</a>' % ( + nodetype.get_absolute_url(), _('View')) + get_link.allow_tags = True + get_link.short_description = _('View on site') + + def get_short_url(self, nodetype): + """Return the short url in HTML""" + short_url = nodetype.short_url + if not short_url: + return _('Unavailable') + return '<a href="%(url)s" target="blank">%(url)s</a>' % \ + {'url': short_url} + get_short_url.allow_tags = True + get_short_url.short_description = _('short url') + + # Custom Methods + def save_model(self, request, nodetype, form, change): + """Save the authors, update time, make an excerpt""" + if not form.cleaned_data.get('excerpt') and nodetype.status == PUBLISHED: + nodetype.excerpt = truncate_words(strip_tags(nodetype.content), 50) + + if nodetype.pk and not request.user.has_perm('gstudio.can_change_author'): + form.cleaned_data['authors'] = nodetype.authors.all() + + if not form.cleaned_data.get('authors'): + form.cleaned_data['authors'].append(request.user) + + nodetype.last_update = datetime.now() + nodetype.save() + + def queryset(self, request): + """Make special filtering by user permissions""" + queryset = super(ObjecttypeAdmin, self).queryset(request) + if request.user.has_perm('gstudio.can_view_all'): + return queryset + return request.user.nodetypes.all() + + def formfield_for_manytomany(self, db_field, request, **kwargs): + """Filters the disposable authors""" + if db_field.name == 'authors': + if request.user.has_perm('gstudio.can_change_author'): + kwargs['queryset'] = User.objects.filter(is_staff=True) + else: + kwargs['queryset'] = User.objects.filter(pk=request.user.pk) + + return super(ObjecttypeAdmin, self).formfield_for_manytomany( + db_field, request, **kwargs) + + def get_actions(self, request): + """Define user actions by permissions""" + actions = super(ObjecttypeAdmin, self).get_actions(request) + if not request.user.has_perm('gstudio.can_change_author') \ + or not request.user.has_perm('gstudio.can_view_all'): + del actions['make_mine'] + if not settings.PING_DIRECTORIES: + del actions['ping_directories'] + if not settings.USE_TWITTER: + del actions['make_tweet'] + + return actions + + # Custom Actions + def make_mine(self, request, queryset): + """Set the nodetypes to the user""" + for nodetype in queryset: + if request.user not in nodetype.authors.all(): + nodetype.authors.add(request.user) + self.message_user( + request, _('The selected nodetypes now belong to you.')) + make_mine.short_description = _('Set the nodetypes to the user') + + def make_published(self, request, queryset): + """Set nodetypes selected as published""" + queryset.update(status=PUBLISHED) + self.ping_directories(request, queryset, messages=False) + self.message_user( + request, _('The selected nodetypes are now marked as published.')) + make_published.short_description = _('Set nodetypes selected as published') + + def make_hidden(self, request, queryset): + """Set nodetypes selected as hidden""" + queryset.update(status=HIDDEN) + self.message_user( + request, _('The selected nodetypes are now marked as hidden.')) + make_hidden.short_description = _('Set nodetypes selected as hidden') + + def make_tweet(self, request, queryset): + """Post an update on Twitter""" + import tweepy + auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY, + settings.TWITTER_CONSUMER_SECRET) + auth.set_access_token(settings.TWITTER_ACCESS_KEY, + settings.TWITTER_ACCESS_SECRET) + api = tweepy.API(auth) + for nodetype in queryset: + short_url = nodetype.short_url + message = '%s %s' % (nodetype.title[:139 - len(short_url)], short_url) + api.update_status(message) + self.message_user( + request, _('The selected nodetypes have been tweeted.')) + make_tweet.short_description = _('Tweet nodetypes selected') + + def close_comments(self, request, queryset): + """Close the comments for selected nodetypes""" + queryset.update(comment_enabled=False) + self.message_user( + request, _('Comments are now closed for selected nodetypes.')) + close_comments.short_description = _('Close the comments for '\ + 'selected nodetypes') + + def close_pingbacks(self, request, queryset): + """Close the pingbacks for selected nodetypes""" + queryset.update(pingback_enabled=False) + self.message_user( + request, _('Linkbacks are now closed for selected nodetypes.')) + close_pingbacks.short_description = _( + 'Close the linkbacks for selected nodetypes') + + def put_on_top(self, request, queryset): + """Put the selected nodetypes on top at the current date""" + queryset.update(creation_date=datetime.now()) + self.ping_directories(request, queryset, messages=False) + self.message_user(request, _( + 'The selected nodetypes are now set at the current date.')) + put_on_top.short_description = _( + 'Put the selected nodetypes on top at the current date') + + def ping_directories(self, request, queryset, messages=True): + """Ping Directories for selected nodetypes""" + for directory in settings.PING_DIRECTORIES: + pinger = DirectoryPinger(directory, queryset) + pinger.join() + if messages: + success = 0 + for result in pinger.results: + if not result.get('flerror', True): + success += 1 + else: + self.message_user(request, + '%s : %s' % (directory, + result['message'])) + if success: + self.message_user( + request, + _('%(directory)s directory succesfully ' \ + 'pinged %(success)d nodetypes.') % + {'directory': directory, 'success': success}) + ping_directories.short_description = _( + 'Ping Directories for selected nodetypes') + + def get_urls(self): + nodetype_admin_urls = super(ObjecttypeAdmin, self).get_urls() + urls = patterns( + 'django.views.generic.simple', + url(r'^autocomplete_tags/$', 'direct_to_template', + {'template': 'admin/gstudio/nodetype/autocomplete_tags.js', + 'mimetype': 'application/javascript'}, + name='gstudio_nodetype_autocomplete_tags'), + url(r'^wymeditor/$', 'direct_to_template', + {'template': 'admin/gstudio/nodetype/wymeditor.js', + 'mimetype': 'application/javascript'}, + name='gstudio_nodetype_wymeditor'), + url(r'^markitup/$', 'direct_to_template', + {'template': 'admin/gstudio/nodetype/markitup.js', + 'mimetype': 'application/javascript'}, + name='gstudio_nodetype_markitup'),) + return urls + nodetype_admin_urls + + def _media(self): + STATIC_URL = '%sgstudio/' % project_settings.STATIC_URL + media = super(ObjecttypeAdmin, self).media + Media( + css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)}, + js=('%sjs/jquery.js' % STATIC_URL, + '%sjs/jquery.bgiframe.js' % STATIC_URL, + '%sjs/jquery.autocomplete.js' % STATIC_URL, + reverse('admin:gstudio_nodetype_autocomplete_tags'),)) + + if settings.WYSIWYG == 'wymeditor': + media += Media( + js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL, + '%sjs/wymeditor/plugins/hovertools/' + 'jquery.wymeditor.hovertools.js' % STATIC_URL, + reverse('admin:gstudio_nodetype_wymeditor'))) + elif settings.WYSIWYG == 'tinymce': + from tinymce.widgets import TinyMCE + media += TinyMCE().media + Media( + js=(reverse('tinymce-js', args=('admin/gstudio/nodetype',)),)) + elif settings.WYSIWYG == 'markitup': + media += Media( + js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL, + '%sjs/markitup/sets/%s/set.js' % ( + STATIC_URL, settings.MARKUP_LANGUAGE), + reverse('admin:gstudio_nodetype_markitup')), + css={'all': ( + '%sjs/markitup/skins/django/style.css' % STATIC_URL, + '%sjs/markitup/sets/%s/style.css' % ( + STATIC_URL, settings.MARKUP_LANGUAGE))}) + return media + media = property(_media) diff --git a/gstudio/admin/processtype.py b/gstudio/admin/processtype.py new file mode 100644 index 0000000..72a795b --- /dev/null +++ b/gstudio/admin/processtype.py @@ -0,0 +1,349 @@ +"""AttributetypeAdmin for Gstudio""" +from datetime import datetime + +from django.forms import Media +from django.contrib import admin +from django.contrib.auth.models import User +from django.utils.html import strip_tags +from django.utils.text import truncate_words +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns +from django.conf import settings as project_settings +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, NoReverseMatch + +from tagging.models import Tag + +import reversion +from gstudio import settings +from gstudio.managers import HIDDEN +from gstudio.managers import PUBLISHED +from gstudio.ping import DirectoryPinger +from gstudio.admin.forms import ProcesstypeAdminForm + + + + +class ProcesstypeAdmin(reversion.VersionAdmin): + """Admin for Processtype model""" + form = ProcesstypeAdminForm + date_hierarchy = 'creation_date' + fieldsets = ((_('Neighbourhood'), {'fields': ('title','altnames' ,'content', 'parent', + 'image', 'slug','status')}), + (_('Processtype Definiton'), {'fields': ('attributetype_set','relationtype_set'), + 'classes': ('collapse', 'collapse-closed')}), + + (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Options'), {'fields': ('featured', 'excerpt', 'template', + 'authors', + 'creation_date', + 'start_publication', + 'end_publication'), + 'classes': ('collapse', 'collapse-closed')}), + (_('Privacy'), {'fields': ('password', 'login_required',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Publication'), {'fields': ('tags', + 'sites')})) + + + list_filter = ('parent','metatypes', 'authors', 'status', 'featured', + 'login_required', 'comment_enabled', 'pingback_enabled', + 'creation_date', 'start_publication', + 'end_publication', 'sites') + list_display = ('get_title', 'get_authors', 'get_metatypes', + 'get_tags', 'get_sites', + 'get_comments_are_open', 'pingback_enabled', + 'get_is_actual', 'get_is_visible', 'get_link', + 'get_short_url', 'creation_date') + radio_fields = {'template': admin.VERTICAL} + filter_horizontal = ('metatypes', 'authors') + prepopulated_fields = {'slug': ('title', )} + search_fields = ('title', 'excerpt', 'content', 'tags') + actions = ['make_mine', 'make_published', 'make_hidden', + 'close_comments', 'close_pingbacks', + 'ping_directories', 'make_tweet', 'put_on_top'] + actions_on_top = True + actions_on_bottom = True + + def __init__(self, model, admin_site): + self.form.admin_site = admin_site + super(ProcesstypeAdmin, self).__init__(model, admin_site) + + # Custom Display + def get_title(self, processtype): + """Return the title with word count and number of comments""" + title = _('%(title)s (%(word_count)i words)') % \ + {'title': processtype.title, 'word_count': processtype.word_count} + comments = processtype.comments.count() + if comments: + return _('%(title)s (%(comments)i comments)') % \ + {'title': title, 'comments': comments} + return title + get_title.short_description = _('title') + + def get_authors(self, processtype): + """Return the authors in HTML""" + try: + authors = ['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_author_detail', + args=[author.username]), + author.username) for author in processtype.authors.all()] + except NoReverseMatch: + authors = [author.username for author in processtype.authors.all()] + return ', '.join(authors) + get_authors.allow_tags = True + get_authors.short_description = _('author(s)') + + def get_metatypes(self, processtype): + """Return the metatypes linked in HTML""" + try: + metatypes = ['<a href="%s" target="blank">%s</a>' % + (metatype.get_absolute_url(), metatype.title) + for metatype in processtype.metatypes.all()] + except NoReverseMatch: + metatypes = [metatype.title for metatype in + processtype.metatypes.all()] + return ', '.join(metatypes) + get_metatypes.allow_tags = True + get_metatypes.short_description = _('metatype(s)') + + def get_tags(self, processtype): + """Return the tags linked in HTML""" + try: + return ', '.join(['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_tag_detail', + args=[tag.name]), tag.name) + for tag in Tag.processs.get_for_process(processtype)]) + except NoReverseMatch: + return processtype.tags + get_tags.allow_tags = True + get_tags.short_description = _('tag(s)') + + def get_sites(self, processtype): + """Return the sites linked in HTML""" + return ', '.join( + ['<a href="http://%(domain)s" target="blank">%(name)s</a>' % + site.__dict__ for site in processtype.sites.all()]) + get_sites.allow_tags = True + get_sites.short_description = _('site(s)') + + def get_comments_are_open(self, processtype): + """Admin wrapper for processtype.comments_are_open""" + return processtype.comments_are_open + get_comments_are_open.boolean = True + get_comments_are_open.short_description = _('comment enabled') + + def get_is_actual(self, processtype): + """Admin wrapper for processtype.is_actual""" + return processtype.is_actual + get_is_actual.boolean = True + get_is_actual.short_description = _('is actual') + + def get_is_visible(self, processtype): + """Admin wrapper for processtype.is_visible""" + return processtype.is_visible + get_is_visible.boolean = True + get_is_visible.short_description = _('is visible') + + def get_link(self, processtype): + """Return a formated link to the processtype""" + return u'<a href="%s" target="blank">%s</a>' % ( + processtype.get_absolute_url(), _('View')) + get_link.allow_tags = True + get_link.short_description = _('View on site') + + def get_short_url(self, processtype): + """Return the short url in HTML""" + short_url = processtype.short_url + if not short_url: + return _('Unavailable') + return '<a href="%(url)s" target="blank">%(url)s</a>' % \ + {'url': short_url} + get_short_url.allow_tags = True + get_short_url.short_description = _('short url') + + # Custom Methods + def save_model(self, request, processtype, form, change): + """Save the authors, update time, make an excerpt""" + if not form.cleaned_data.get('excerpt') and processtype.status == PUBLISHED: + processtype.excerpt = truncate_words(strip_tags(processtype.content), 50) + + if processtype.pk and not request.user.has_perm('gstudio.can_change_author'): + form.cleaned_data['authors'] = processtype.authors.all() + + if not form.cleaned_data.get('authors'): + form.cleaned_data['authors'].append(request.user) + + processtype.last_update = datetime.now() + processtype.save() + + def queryset(self, request): + """Make special filtering by user permissions""" + queryset = super(ProcesstypeAdmin, self).queryset(request) + if request.user.has_perm('gstudio.can_view_all'): + return queryset + return request.user.processtypes.all() + + def formfield_for_manytomany(self, db_field, request, **kwargs): + """Filters the disposable authors""" + if db_field.name == 'authors': + if request.user.has_perm('gstudio.can_change_author'): + kwargs['queryset'] = User.objects.filter(is_staff=True) + else: + kwargs['queryset'] = User.objects.filter(pk=request.user.pk) + + return super(ProcesstypeAdmin, self).formfield_for_manytomany( + db_field, request, **kwargs) + + def get_actions(self, request): + """Define user actions by permissions""" + actions = super(ProcesstypeAdmin, self).get_actions(request) + if not request.user.has_perm('gstudio.can_change_author') \ + or not request.user.has_perm('gstudio.can_view_all'): + del actions['make_mine'] + if not settings.PING_DIRECTORIES: + del actions['ping_directories'] + if not settings.USE_TWITTER: + del actions['make_tweet'] + + return actions + + # Custom Actions + def make_mine(self, request, queryset): + """Set the processtypes to the user""" + for processtype in queryset: + if request.user not in processtype.authors.all(): + processtype.authors.add(request.user) + self.message_user( + request, _('The selected processtypes now belong to you.')) + make_mine.short_description = _('Set the processtypes to the user') + + def make_published(self, request, queryset): + """Set processtypes selected as published""" + queryset.update(status=PUBLISHED) + self.ping_directories(request, queryset, messages=False) + self.message_user( + request, _('The selected processtypes are now marked as published.')) + make_published.short_description = _('Set processtypes selected as published') + + def make_hidden(self, request, queryset): + """Set processtypes selected as hidden""" + queryset.update(status=HIDDEN) + self.message_user( + request, _('The selected processtypes are now marked as hidden.')) + make_hidden.short_description = _('Set processtypes selected as hidden') + + def make_tweet(self, request, queryset): + """Post an update on Twitter""" + import tweepy + auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY, + settings.TWITTER_CONSUMER_SECRET) + auth.set_access_token(settings.TWITTER_ACCESS_KEY, + settings.TWITTER_ACCESS_SECRET) + api = tweepy.API(auth) + for processtype in queryset: + short_url = processtype.short_url + message = '%s %s' % (processtype.title[:139 - len(short_url)], short_url) + api.update_status(message) + self.message_user( + request, _('The selected processtypes have been tweeted.')) + make_tweet.short_description = _('Tweet processtypes selected') + + def close_comments(self, request, queryset): + """Close the comments for selected processtypes""" + queryset.update(comment_enabled=False) + self.message_user( + request, _('Comments are now closed for selected processtypes.')) + close_comments.short_description = _('Close the comments for '\ + 'selected processtypes') + + def close_pingbacks(self, request, queryset): + """Close the pingbacks for selected processtypes""" + queryset.update(pingback_enabled=False) + self.message_user( + request, _('Linkbacks are now closed for selected processtypes.')) + close_pingbacks.short_description = _( + 'Close the linkbacks for selected processtypes') + + def put_on_top(self, request, queryset): + """Put the selected processtypes on top at the current date""" + queryset.update(creation_date=datetime.now()) + self.ping_directories(request, queryset, messages=False) + self.message_user(request, _( + 'The selected processtypes are now set at the current date.')) + put_on_top.short_description = _( + 'Put the selected processtypes on top at the current date') + + def ping_directories(self, request, queryset, messages=True): + """Ping Directories for selected processtypes""" + for directory in settings.PING_DIRECTORIES: + pinger = DirectoryPinger(directory, queryset) + pinger.join() + if messages: + success = 0 + for result in pinger.results: + if not result.get('flerror', True): + success += 1 + else: + self.message_user(request, + '%s : %s' % (directory, + result['message'])) + if success: + self.message_user( + request, + _('%(directory)s directory succesfully ' \ + 'pinged %(success)d processtypes.') % + {'directory': directory, 'success': success}) + ping_directories.short_description = _( + 'Ping Directories for selected processtypes') + + def get_urls(self): + processtype_admin_urls = super(ProcesstypeAdmin, self).get_urls() + urls = patterns( + 'django.views.generic.simple', + url(r'^autocomplete_tags/$', 'direct_to_template', + {'template': 'admin/gstudio/processtype/autocomplete_tags.js', + 'mimetype': 'application/javascript'}, + name='gstudio_processtype_autocomplete_tags'), + url(r'^wymeditor/$', 'direct_to_template', + {'template': 'admin/gstudio/processtype/wymeditor.js', + 'mimetype': 'application/javascript'}, + name='gstudio_processtype_wymeditor'), + url(r'^markitup/$', 'direct_to_template', + {'template': 'admin/gstudio/processtype/markitup.js', + 'mimetype': 'application/javascript'}, + name='gstudio_processtype_markitup'),) + return urls + processtype_admin_urls + + def _media(self): + STATIC_URL = '%sgstudio/' % project_settings.STATIC_URL + media = super(ProcesstypeAdmin, self).media + Media( + css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)}, + js=('%sjs/jquery.js' % STATIC_URL, + '%sjs/jquery.bgiframe.js' % STATIC_URL, + '%sjs/jquery.autocomplete.js' % STATIC_URL, + reverse('admin:gstudio_processtype_autocomplete_tags'),)) + + if settings.WYSIWYG == 'wymeditor': + media += Media( + js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL, + '%sjs/wymeditor/plugins/hovertools/' + 'jquery.wymeditor.hovertools.js' % STATIC_URL, + reverse('admin:gstudio_processtype_wymeditor'))) + elif settings.WYSIWYG == 'tinymce': + from tinymce.widgets import TinyMCE + media += TinyMCE().media + Media( + js=(reverse('tinymce-js', args=('admin/gstudio/processtype',)),)) + elif settings.WYSIWYG == 'markitup': + media += Media( + js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL, + '%sjs/markitup/sets/%s/set.js' % ( + STATIC_URL, settings.MARKUP_LANGUAGE), + reverse('admin:gstudio_processtype_markitup')), + css={'all': ( + '%sjs/markitup/skins/django/style.css' % STATIC_URL, + '%sjs/markitup/sets/%s/style.css' % ( + STATIC_URL, settings.MARKUP_LANGUAGE))}) + return media + media = property(_media) diff --git a/gstudio/admin/relation.py b/gstudio/admin/relation.py new file mode 100644 index 0000000..04b4e8b --- /dev/null +++ b/gstudio/admin/relation.py @@ -0,0 +1,13 @@ +"""MetatypeAdmin for Gstudio""" +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import RelationAdminForm +import reversion + +class RelationAdmin(reversion.VersionAdmin): + def save_model(self, request, relation, form, change): + relation.title = relation.composed_sentence + relation.save() + diff --git a/gstudio/admin/relationspecification.py b/gstudio/admin/relationspecification.py new file mode 100644 index 0000000..5da162b --- /dev/null +++ b/gstudio/admin/relationspecification.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import RelationSpecificationAdminForm +import reversion + +class RelationSpecificationAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/relationtype.py b/gstudio/admin/relationtype.py new file mode 100644 index 0000000..374f9a8 --- /dev/null +++ b/gstudio/admin/relationtype.py @@ -0,0 +1,362 @@ +"""RelationtypeAdmin for Gstudio""" +from datetime import datetime +from django.forms import Media +from django.contrib import admin +from django.contrib.auth.models import User +from django.utils.html import strip_tags +from django.utils.text import truncate_words +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns +from django.conf import settings as project_settings +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, NoReverseMatch + +from tagging.models import Tag + +import reversion +from gstudio import settings +from gstudio.managers import HIDDEN +from gstudio.managers import PUBLISHED +from gstudio.ping import DirectoryPinger +from gstudio.admin.forms import RelationtypeAdminForm + + + + +class RelationtypeAdmin(reversion.VersionAdmin): + """Admin for Relationtype model""" + form = RelationtypeAdminForm + date_hierarchy = 'creation_date' + fieldsets = (( + (_('Neighbourhood Definiton'), {'fields': ( + 'title', + 'inverse', + 'altnames', + 'parent', + 'slug', + + 'left_subjecttype', + 'left_applicable_nodetypes', + 'left_cardinality', + 'right_subjecttype', + 'right_applicable_nodetypes', + 'right_cardinality', + 'is_symmetrical', + 'is_reflexive', + 'is_transitive') + }), + (_('Content'), {'fields': ('content', 'image',), + 'classes': ('collapse', 'collapse-closed')}), + + + (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Options'), {'fields': ('featured', 'excerpt', 'template', + 'authors', + 'creation_date', + 'start_publication', + 'end_publication'), + 'classes': ('collapse', 'collapse-closed')}), + )) + + + list_filter = ('parent','metatypes', 'authors', 'status', 'featured', + 'login_required', 'comment_enabled', 'pingback_enabled', + 'creation_date', 'start_publication', + 'end_publication', 'sites') + list_display = ('get_title', 'get_authors', 'get_metatypes', + 'get_tags', 'get_sites', + 'get_comments_are_open', 'pingback_enabled', + 'get_is_actual', 'get_is_visible', 'get_link', + 'get_short_url', 'creation_date') + radio_fields = {'template': admin.VERTICAL} + filter_horizontal = ('metatypes', 'authors') + prepopulated_fields = {'slug': ('title', )} + search_fields = ('title', 'excerpt', 'content', 'tags') + actions = ['make_mine', 'make_published', 'make_hidden', + 'close_comments', 'close_pingbacks', + 'ping_directories', 'make_tweet', 'put_on_top'] + actions_on_top = True + actions_on_bottom = True + + def __init__(self, model, admin_site): + self.form.admin_site = admin_site + super(RelationtypeAdmin, self).__init__(model, admin_site) + + # Custom Display + def get_title(self, relationtype): + """Return the title with word count and number of comments""" + title = _('%(title)s (%(word_count)i words)') % \ + {'title': relationtype.title, 'word_count': relationtype.word_count} + comments = relationtype.comments.count() + if comments: + return _('%(title)s (%(comments)i comments)') % \ + {'title': title, 'comments': comments} + return title + get_title.short_description = _('title') + + def get_authors(self, relationtype): + """Return the authors in HTML""" + try: + authors = ['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_author_detail', + args=[author.username]), + author.username) for author in relationtype.authors.all()] + except NoReverseMatch: + authors = [author.username for author in relationtype.authors.all()] + return ', '.join(authors) + get_authors.allow_tags = True + get_authors.short_description = _('author(s)') + + def get_metatypes(self, relationtype): + """Return the metatypes linked in HTML""" + try: + metatypes = ['<a href="%s" target="blank">%s</a>' % + (metatype.get_absolute_url(), metatype.title) + for metatype in relationtype.metatypes.all()] + except NoReverseMatch: + metatypes = [metatype.title for metatype in + relationtype.metatypes.all()] + return ', '.join(metatypes) + get_metatypes.allow_tags = True + get_metatypes.short_description = _('metatype(s)') + + def get_tags(self, relationtype): + """Return the tags linked in HTML""" + try: + return ', '.join(['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_tag_detail', + args=[tag.name]), tag.name) + for tag in Tag.relations.get_for_relation(relationtype)]) + except NoReverseMatch: + return relationtype.tags + get_tags.allow_tags = True + get_tags.short_description = _('tag(s)') + + def get_sites(self, relationtype): + """Return the sites linked in HTML""" + return ', '.join( + ['<a href="http://%(domain)s" target="blank">%(name)s</a>' % + site.__dict__ for site in relationtype.sites.all()]) + get_sites.allow_tags = True + get_sites.short_description = _('site(s)') + + def get_comments_are_open(self, relationtype): + """Admin wrapper for relationtype.comments_are_open""" + return relationtype.comments_are_open + get_comments_are_open.boolean = True + get_comments_are_open.short_description = _('comment enabled') + + def get_is_actual(self, relationtype): + """Admin wrapper for relationtype.is_actual""" + return relationtype.is_actual + get_is_actual.boolean = True + get_is_actual.short_description = _('is actual') + + def get_is_visible(self, relationtype): + """Admin wrapper for relationtype.is_visible""" + return relationtype.is_visible + get_is_visible.boolean = True + get_is_visible.short_description = _('is visible') + + def get_link(self, relationtype): + """Return a formated link to the relationtype""" + return u'<a href="%s" target="blank">%s</a>' % ( + relationtype.get_absolute_url(), _('View')) + get_link.allow_tags = True + get_link.short_description = _('View on site') + + def get_short_url(self, relationtype): + """Return the short url in HTML""" + short_url = relationtype.short_url + if not short_url: + return _('Unavailable') + return '<a href="%(url)s" target="blank">%(url)s</a>' % \ + {'url': short_url} + get_short_url.allow_tags = True + get_short_url.short_description = _('short url') + + # Custom Methods + def save_model(self, request, relationtype, form, change): + """Save the authors, update time, make an excerpt""" + if not form.cleaned_data.get('excerpt') and relationtype.status == PUBLISHED: + relationtype.excerpt = truncate_words(strip_tags(relationtype.content), 50) + + if relationtype.pk and not request.user.has_perm('gstudio.can_change_author'): + form.cleaned_data['authors'] = relationtype.authors.all() + + if not form.cleaned_data.get('authors'): + form.cleaned_data['authors'].append(request.user) + + relationtype.last_update = datetime.now() + relationtype.save() + + def queryset(self, request): + """Make special filtering by user permissions""" + queryset = super(RelationtypeAdmin, self).queryset(request) + if request.user.has_perm('gstudio.can_view_all'): + return queryset + return request.user.relationtypes.all() + + def formfield_for_manytomany(self, db_field, request, **kwargs): + """Filters the disposable authors""" + if db_field.name == 'authors': + if request.user.has_perm('gstudio.can_change_author'): + kwargs['queryset'] = User.objects.filter(is_staff=True) + else: + kwargs['queryset'] = User.objects.filter(pk=request.user.pk) + + return super(RelationtypeAdmin, self).formfield_for_manytomany( + db_field, request, **kwargs) + + def get_actions(self, request): + """Define user actions by permissions""" + actions = super(RelationtypeAdmin, self).get_actions(request) + if not request.user.has_perm('gstudio.can_change_author') \ + or not request.user.has_perm('gstudio.can_view_all'): + del actions['make_mine'] + if not settings.PING_DIRECTORIES: + del actions['ping_directories'] + if not settings.USE_TWITTER: + del actions['make_tweet'] + + return actions + + # Custom Actions + def make_mine(self, request, queryset): + """Set the relationtypes to the user""" + for relationtype in queryset: + if request.user not in relationtype.authors.all(): + relationtype.authors.add(request.user) + self.message_user( + request, _('The selected relationtypes now belong to you.')) + make_mine.short_description = _('Set the relationtypes to the user') + + def make_published(self, request, queryset): + """Set relationtypes selected as published""" + queryset.update(status=PUBLISHED) + self.ping_directories(request, queryset, messages=False) + self.message_user( + request, _('The selected relationtypes are now marked as published.')) + make_published.short_description = _('Set relationtypes selected as published') + + def make_hidden(self, request, queryset): + """Set relationtypes selected as hidden""" + queryset.update(status=HIDDEN) + self.message_user( + request, _('The selected relationtypes are now marked as hidden.')) + make_hidden.short_description = _('Set relationtypes selected as hidden') + + def make_tweet(self, request, queryset): + """Post an update on Twitter""" + import tweepy + auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY, + settings.TWITTER_CONSUMER_SECRET) + auth.set_access_token(settings.TWITTER_ACCESS_KEY, + settings.TWITTER_ACCESS_SECRET) + api = tweepy.API(auth) + for relationtype in queryset: + short_url = relationtype.short_url + message = '%s %s' % (relationtype.title[:139 - len(short_url)], short_url) + api.update_status(message) + self.message_user( + request, _('The selected relationtypes have been tweeted.')) + make_tweet.short_description = _('Tweet relationtypes selected') + + def close_comments(self, request, queryset): + """Close the comments for selected relationtypes""" + queryset.update(comment_enabled=False) + self.message_user( + request, _('Comments are now closed for selected relationtypes.')) + close_comments.short_description = _('Close the comments for '\ + 'selected relationtypes') + + def close_pingbacks(self, request, queryset): + """Close the pingbacks for selected relationtypes""" + queryset.update(pingback_enabled=False) + self.message_user( + request, _('Linkbacks are now closed for selected relationtypes.')) + close_pingbacks.short_description = _( + 'Close the linkbacks for selected relationtypes') + + def put_on_top(self, request, queryset): + """Put the selected relationtypes on top at the current date""" + queryset.update(creation_date=datetime.now()) + self.ping_directories(request, queryset, messages=False) + self.message_user(request, _( + 'The selected relationtypes are now set at the current date.')) + put_on_top.short_description = _( + 'Put the selected relationtypes on top at the current date') + + def ping_directories(self, request, queryset, messages=True): + """Ping Directories for selected relationtypes""" + for directory in settings.PING_DIRECTORIES: + pinger = DirectoryPinger(directory, queryset) + pinger.join() + if messages: + success = 0 + for result in pinger.results: + if not result.get('flerror', True): + success += 1 + else: + self.message_user(request, + '%s : %s' % (directory, + result['message'])) + if success: + self.message_user( + request, + _('%(directory)s directory succesfully ' \ + 'pinged %(success)d relationtypes.') % + {'directory': directory, 'success': success}) + ping_directories.short_description = _( + 'Ping Directories for selected relationtypes') + + def get_urls(self): + relationtype_admin_urls = super(RelationtypeAdmin, self).get_urls() + urls = patterns( + 'django.views.generic.simple', + url(r'^autocomplete_tags/$', 'direct_to_template', + {'template': 'admin/gstudio/relationtype/autocomplete_tags.js', + 'mimetype': 'application/javascript'}, + name='gstudio_relationtype_autocomplete_tags'), + url(r'^wymeditor/$', 'direct_to_template', + {'template': 'admin/gstudio/relationtype/wymeditor.js', + 'mimetype': 'application/javascript'}, + name='gstudio_relationtype_wymeditor'), + url(r'^markitup/$', 'direct_to_template', + {'template': 'admin/gstudio/relationtype/markitup.js', + 'mimetype': 'application/javascript'}, + name='gstudio_relationtype_markitup'),) + return urls + relationtype_admin_urls + + def _media(self): + STATIC_URL = '%sgstudio/' % project_settings.STATIC_URL + media = super(RelationtypeAdmin, self).media + Media( + css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)}, + js=('%sjs/jquery.js' % STATIC_URL, + '%sjs/jquery.bgiframe.js' % STATIC_URL, + '%sjs/jquery.autocomplete.js' % STATIC_URL, + reverse('admin:gstudio_relationtype_autocomplete_tags'),)) + + if settings.WYSIWYG == 'wymeditor': + media += Media( + js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL, + '%sjs/wymeditor/plugins/hovertools/' + 'jquery.wymeditor.hovertools.js' % STATIC_URL, + reverse('admin:gstudio_relationtype_wymeditor'))) + elif settings.WYSIWYG == 'tinymce': + from tinymce.widgets import TinyMCE + media += TinyMCE().media + Media( + js=(reverse('tinymce-js', args=('admin/gstudio/relationtype',)),)) + elif settings.WYSIWYG == 'markitup': + media += Media( + js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL, + '%sjs/markitup/sets/%s/set.js' % ( + STATIC_URL, settings.MARKUP_LANGUAGE), + reverse('admin:gstudio_relationtype_markitup')), + css={'all': ( + '%sjs/markitup/skins/django/style.css' % STATIC_URL, + '%sjs/markitup/sets/%s/style.css' % ( + STATIC_URL, settings.MARKUP_LANGUAGE))}) + return media + media = property(_media) diff --git a/gstudio/admin/systemtype.py b/gstudio/admin/systemtype.py new file mode 100644 index 0000000..8a12856 --- /dev/null +++ b/gstudio/admin/systemtype.py @@ -0,0 +1,352 @@ +"""AttributetypeAdmin for Gstudio""" +from datetime import datetime + +from django.forms import Media +from django.contrib import admin +from django.contrib.auth.models import User +from django.utils.html import strip_tags +from django.utils.text import truncate_words +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns +from django.conf import settings as project_settings +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, NoReverseMatch + +from tagging.models import Tag + +import reversion +from gstudio import settings +from gstudio.managers import HIDDEN +from gstudio.managers import PUBLISHED +from gstudio.ping import DirectoryPinger +from gstudio.admin.forms import SystemtypeAdminForm + + + + +class SystemtypeAdmin(reversion.VersionAdmin): + """Admin for Systemtype model""" + form = SystemtypeAdminForm + date_hierarchy = 'creation_date' + + fieldsets = ((_('Neighbourhood'), {'fields': ('title', 'altnames','content', 'parent','image', 'slug','status')}), + (_('Systemtype definiton'), {'fields': ( + 'nodetype_set', + 'relationtype_set', + 'attributetype_set', + 'metatype_set', + 'processtype_set')}), + (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Options'), {'fields': ('featured', 'excerpt', 'template', + 'authors', + 'creation_date', + 'start_publication', + 'end_publication'), + 'classes': ('collapse', 'collapse-closed')}), + (_('Privacy'), {'fields': ('password', 'login_required',), + 'classes': ('collapse', 'collapse-closed')}), + (_('Publication'), {'fields': ('tags', + 'sites')})) + + + list_filter = ('parent','metatypes', 'authors', 'status', 'featured', + 'login_required', 'comment_enabled', 'pingback_enabled', + 'creation_date', 'start_publication', + 'end_publication', 'sites') + list_display = ('get_title', 'get_authors', 'get_metatypes', + 'get_tags', 'get_sites', + 'get_comments_are_open', 'pingback_enabled', + 'get_is_actual', 'get_is_visible', 'get_link', + 'get_short_url', 'creation_date') + radio_fields = {'template': admin.VERTICAL} + filter_horizontal = ('metatypes', 'authors') + prepopulated_fields = {'slug': ('title', )} + search_fields = ('title', 'excerpt', 'content', 'tags') + actions = ['make_mine', 'make_published', 'make_hidden', + 'close_comments', 'close_pingbacks', + 'ping_directories', 'make_tweet', 'put_on_top'] + actions_on_top = True + actions_on_bottom = True + + def __init__(self, model, admin_site): + self.form.admin_site = admin_site + super(SystemtypeAdmin, self).__init__(model, admin_site) + + # Custom Display + def get_title(self, systemtype): + """Return the title with word count and number of comments""" + title = _('%(title)s (%(word_count)i words)') % \ + {'title': systemtype.title, 'word_count': systemtype.word_count} + comments = systemtype.comments.count() + if comments: + return _('%(title)s (%(comments)i comments)') % \ + {'title': title, 'comments': comments} + return title + get_title.short_description = _('title') + + def get_authors(self, systemtype): + """Return the authors in HTML""" + try: + authors = ['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_author_detail', + args=[author.username]), + author.username) for author in systemtype.authors.all()] + except NoReverseMatch: + authors = [author.username for author in systemtype.authors.all()] + return ', '.join(authors) + get_authors.allow_tags = True + get_authors.short_description = _('author(s)') + + def get_metatypes(self, systemtype): + """Return the metatypes linked in HTML""" + try: + metatypes = ['<a href="%s" target="blank">%s</a>' % + (metatype.get_absolute_url(), metatype.title) + for metatype in systemtype.metatypes.all()] + except NoReverseMatch: + metatypes = [metatype.title for metatype in + systemtype.metatypes.all()] + return ', '.join(metatypes) + get_metatypes.allow_tags = True + get_metatypes.short_description = _('metatype(s)') + + def get_tags(self, systemtype): + """Return the tags linked in HTML""" + try: + return ', '.join(['<a href="%s" target="blank">%s</a>' % + (reverse('gstudio_tag_detail', + args=[tag.name]), tag.name) + for tag in Tag.systems.get_for_system(systemtype)]) + except NoReverseMatch: + return systemtype.tags + get_tags.allow_tags = True + get_tags.short_description = _('tag(s)') + + def get_sites(self, systemtype): + """Return the sites linked in HTML""" + return ', '.join( + ['<a href="http://%(domain)s" target="blank">%(name)s</a>' % + site.__dict__ for site in systemtype.sites.all()]) + get_sites.allow_tags = True + get_sites.short_description = _('site(s)') + + def get_comments_are_open(self, systemtype): + """Admin wrapper for systemtype.comments_are_open""" + return systemtype.comments_are_open + get_comments_are_open.boolean = True + get_comments_are_open.short_description = _('comment enabled') + + def get_is_actual(self, systemtype): + """Admin wrapper for systemtype.is_actual""" + return systemtype.is_actual + get_is_actual.boolean = True + get_is_actual.short_description = _('is actual') + + def get_is_visible(self, systemtype): + """Admin wrapper for systemtype.is_visible""" + return systemtype.is_visible + get_is_visible.boolean = True + get_is_visible.short_description = _('is visible') + + def get_link(self, systemtype): + """Return a formated link to the systemtype""" + return u'<a href="%s" target="blank">%s</a>' % ( + systemtype.get_absolute_url(), _('View')) + get_link.allow_tags = True + get_link.short_description = _('View on site') + + def get_short_url(self, systemtype): + """Return the short url in HTML""" + short_url = systemtype.short_url + if not short_url: + return _('Unavailable') + return '<a href="%(url)s" target="blank">%(url)s</a>' % \ + {'url': short_url} + get_short_url.allow_tags = True + get_short_url.short_description = _('short url') + + # Custom Methods + def save_model(self, request, systemtype, form, change): + """Save the authors, update time, make an excerpt""" + if not form.cleaned_data.get('excerpt') and systemtype.status == PUBLISHED: + systemtype.excerpt = truncate_words(strip_tags(systemtype.content), 50) + + if systemtype.pk and not request.user.has_perm('gstudio.can_change_author'): + form.cleaned_data['authors'] = systemtype.authors.all() + + if not form.cleaned_data.get('authors'): + form.cleaned_data['authors'].append(request.user) + + systemtype.last_update = datetime.now() + systemtype.save() + + def queryset(self, request): + """Make special filtering by user permissions""" + queryset = super(SystemtypeAdmin, self).queryset(request) + if request.user.has_perm('gstudio.can_view_all'): + return queryset + return request.user.systemtypes.all() + + def formfield_for_manytomany(self, db_field, request, **kwargs): + """Filters the disposable authors""" + if db_field.name == 'authors': + if request.user.has_perm('gstudio.can_change_author'): + kwargs['queryset'] = User.objects.filter(is_staff=True) + else: + kwargs['queryset'] = User.objects.filter(pk=request.user.pk) + + return super(SystemtypeAdmin, self).formfield_for_manytomany( + db_field, request, **kwargs) + + def get_actions(self, request): + """Define user actions by permissions""" + actions = super(SystemtypeAdmin, self).get_actions(request) + if not request.user.has_perm('gstudio.can_change_author') \ + or not request.user.has_perm('gstudio.can_view_all'): + del actions['make_mine'] + if not settings.PING_DIRECTORIES: + del actions['ping_directories'] + if not settings.USE_TWITTER: + del actions['make_tweet'] + + return actions + + # Custom Actions + def make_mine(self, request, queryset): + """Set the systemtypes to the user""" + for systemtype in queryset: + if request.user not in systemtype.authors.all(): + systemtype.authors.add(request.user) + self.message_user( + request, _('The selected systemtypes now belong to you.')) + make_mine.short_description = _('Set the systemtypes to the user') + + def make_published(self, request, queryset): + """Set systemtypes selected as published""" + queryset.update(status=PUBLISHED) + self.ping_directories(request, queryset, messages=False) + self.message_user( + request, _('The selected systemtypes are now marked as published.')) + make_published.short_description = _('Set systemtypes selected as published') + + def make_hidden(self, request, queryset): + """Set systemtypes selected as hidden""" + queryset.update(status=HIDDEN) + self.message_user( + request, _('The selected systemtypes are now marked as hidden.')) + make_hidden.short_description = _('Set systemtypes selected as hidden') + + def make_tweet(self, request, queryset): + """Post an update on Twitter""" + import tweepy + auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY, + settings.TWITTER_CONSUMER_SECRET) + auth.set_access_token(settings.TWITTER_ACCESS_KEY, + settings.TWITTER_ACCESS_SECRET) + api = tweepy.API(auth) + for systemtype in queryset: + short_url = systemtype.short_url + message = '%s %s' % (systemtype.title[:139 - len(short_url)], short_url) + api.update_status(message) + self.message_user( + request, _('The selected systemtypes have been tweeted.')) + make_tweet.short_description = _('Tweet systemtypes selected') + + def close_comments(self, request, queryset): + """Close the comments for selected systemtypes""" + queryset.update(comment_enabled=False) + self.message_user( + request, _('Comments are now closed for selected systemtypes.')) + close_comments.short_description = _('Close the comments for '\ + 'selected systemtypes') + + def close_pingbacks(self, request, queryset): + """Close the pingbacks for selected systemtypes""" + queryset.update(pingback_enabled=False) + self.message_user( + request, _('Linkbacks are now closed for selected systemtypes.')) + close_pingbacks.short_description = _( + 'Close the linkbacks for selected systemtypes') + + def put_on_top(self, request, queryset): + """Put the selected systemtypes on top at the current date""" + queryset.update(creation_date=datetime.now()) + self.ping_directories(request, queryset, messages=False) + self.message_user(request, _( + 'The selected systemtypes are now set at the current date.')) + put_on_top.short_description = _( + 'Put the selected systemtypes on top at the current date') + + def ping_directories(self, request, queryset, messages=True): + """Ping Directories for selected systemtypes""" + for directory in settings.PING_DIRECTORIES: + pinger = DirectoryPinger(directory, queryset) + pinger.join() + if messages: + success = 0 + for result in pinger.results: + if not result.get('flerror', True): + success += 1 + else: + self.message_user(request, + '%s : %s' % (directory, + result['message'])) + if success: + self.message_user( + request, + _('%(directory)s directory succesfully ' \ + 'pinged %(success)d systemtypes.') % + {'directory': directory, 'success': success}) + ping_directories.short_description = _( + 'Ping Directories for selected systemtypes') + + def get_urls(self): + systemtype_admin_urls = super(SystemtypeAdmin, self).get_urls() + urls = patterns( + 'django.views.generic.simple', + url(r'^autocomplete_tags/$', 'direct_to_template', + {'template': 'admin/gstudio/systemtype/autocomplete_tags.js', + 'mimetype': 'application/javascript'}, + name='gstudio_systemtype_autocomplete_tags'), + url(r'^wymeditor/$', 'direct_to_template', + {'template': 'admin/gstudio/systemtype/wymeditor.js', + 'mimetype': 'application/javascript'}, + name='gstudio_systemtype_wymeditor'), + url(r'^markitup/$', 'direct_to_template', + {'template': 'admin/gstudio/systemtype/markitup.js', + 'mimetype': 'application/javascript'}, + name='gstudio_systemtype_markitup'),) + return urls + systemtype_admin_urls + + def _media(self): + STATIC_URL = '%sgstudio/' % project_settings.STATIC_URL + media = super(SystemtypeAdmin, self).media + Media( + css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)}, + js=('%sjs/jquery.js' % STATIC_URL, + '%sjs/jquery.bgiframe.js' % STATIC_URL, + '%sjs/jquery.autocomplete.js' % STATIC_URL, + reverse('admin:gstudio_systemtype_autocomplete_tags'),)) + + if settings.WYSIWYG == 'wymeditor': + media += Media( + js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL, + '%sjs/wymeditor/plugins/hovertools/' + 'jquery.wymeditor.hovertools.js' % STATIC_URL, + reverse('admin:gstudio_systemtype_wymeditor'))) + elif settings.WYSIWYG == 'tinymce': + from tinymce.widgets import TinyMCE + media += TinyMCE().media + Media( + js=(reverse('tinymce-js', args=('admin/gstudio/systemtype',)),)) + elif settings.WYSIWYG == 'markitup': + media += Media( + js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL, + '%sjs/markitup/sets/%s/set.js' % ( + STATIC_URL, settings.MARKUP_LANGUAGE), + reverse('admin:gstudio_systemtype_markitup')), + css={'all': ( + '%sjs/markitup/skins/django/style.css' % STATIC_URL, + '%sjs/markitup/sets/%s/style.css' % ( + STATIC_URL, settings.MARKUP_LANGUAGE))}) + return media + media = property(_media) diff --git a/gstudio/admin/union.py b/gstudio/admin/union.py new file mode 100644 index 0000000..0836936 --- /dev/null +++ b/gstudio/admin/union.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.core.urlresolvers import NoReverseMatch +from django.utils.translation import ugettext_lazy as _ + +from gstudio.admin.forms import UnionAdminForm +import reversion + +class UnionAdmin(reversion.VersionAdmin): + pass diff --git a/gstudio/admin/widgets.py b/gstudio/admin/widgets.py new file mode 100644 index 0000000..bc6a962 --- /dev/null +++ b/gstudio/admin/widgets.py @@ -0,0 +1,107 @@ +"""Widgets for Gstudio admin""" +from itertools import chain + +from django import forms +from django.conf import settings +from django.contrib.admin import widgets +from django.utils.html import escape +from django.utils.html import conditional_escape +from django.utils.encoding import smart_unicode +from django.utils.encoding import force_unicode + + +class TreeNodeChoiceField(forms.ModelChoiceField): + """Duplicating the TreeNodeChoiceField bundled in django-mptt + to avoid conflict with the TreeNodeChoiceField bundled in django-cms...""" + def __init__(self, level_indicator=u'|--', *args, **kwargs): + self.level_indicator = level_indicator + if kwargs.get('required', True) and not 'empty_label' in kwargs: + kwargs['empty_label'] = None + super(TreeNodeChoiceField, self).__init__(*args, **kwargs) + + def label_from_instance(self, obj): + """Creates labels which represent the tree level of each node + when generating option labels.""" + return u'%s %s' % (self.level_indicator * getattr( + obj, obj._mptt_meta.level_attr), smart_unicode(obj)) + + +class MPTTModelChoiceIterator(forms.models.ModelChoiceIterator): + """MPTT version of ModelChoiceIterator""" + def choice(self, obj): + """Overriding choice method""" + tree_id = getattr(obj, self.queryset.model._mptt_meta.tree_id_attr, 0) + left = getattr(obj, self.queryset.model._mptt_meta.left_attr, 0) + return super(MPTTModelChoiceIterator, + self).choice(obj) + ((tree_id, left),) + + +class MPTTModelMultipleChoiceField(forms.ModelMultipleChoiceField): + """MPTT version of ModelMultipleChoiceField""" + def __init__(self, level_indicator=u'|--', *args, **kwargs): + self.level_indicator = level_indicator + super(MPTTModelMultipleChoiceField, self).__init__(*args, **kwargs) + + def label_from_instance(self, obj): + """Creates labels which represent the tree level of each node + when generating option labels.""" + return u'%s %s' % (self.level_indicator * getattr( + obj, obj._mptt_meta.level_attr), smart_unicode(obj)) + + def _get_choices(self): + """Overriding _get_choices""" + if hasattr(self, '_choices'): + return self._choices + return MPTTModelChoiceIterator(self) + + choices = property(_get_choices, forms.ChoiceField._set_choices) + + +class MPTTFilteredSelectMultiple(widgets.FilteredSelectMultiple): + """MPTT version of FilteredSelectMultiple""" + def __init__(self, verbose_name, is_stacked, attrs=None, choices=()): + super(MPTTFilteredSelectMultiple, self).__init__( + verbose_name, is_stacked, attrs, choices) + + def render_options(self, choices, selected_choices): + """ + This is copy'n'pasted from django.forms.widgets Select(Widget) + change to the for loop and render_option so they will unpack + and use our extra tuple of mptt sort fields (if you pass in + some default choices for this field, make sure they have the + extra tuple too!) + """ + def render_option(option_value, option_label, sort_fields): + """Inner scope render_option""" + option_value = force_unicode(option_value) + selected_html = (option_value in selected_choices) \ + and u' selected="selected"' or '' + return u'<option value="%s" data-tree-id="%s" ' \ + 'data-left-value="%s"%s>%s</option>' % ( + escape(option_value), + sort_fields[0], + sort_fields[1], + selected_html, + conditional_escape(force_unicode(option_label)), + ) + # Normalize to strings. + selected_choices = set([force_unicode(v) for v in selected_choices]) + output = [] + for option_value, option_label, sort_fields in chain( + self.choices, choices): + if isinstance(option_label, (list, tuple)): + output.append(u'<optgroup label="%s">' % escape( + force_unicode(option_value))) + for option in option_label: + output.append(render_option(*option)) + output.append(u'</optgroup>') + else: + output.append(render_option(option_value, option_label, + sort_fields)) + return u'\n'.join(output) + + class Media: + """MPTTFilteredSelectMultiple's Media""" + js = (settings.ADMIN_MEDIA_PREFIX + 'js/core.js', + settings.STATIC_URL + 'gstudio/js/mptt_m2m_selectbox.js', + settings.ADMIN_MEDIA_PREFIX + 'js/SelectFilter2.js',) |