diff --git a/make_mozilla/projects/admin.py b/make_mozilla/projects/admin.py index 9935eec..886ceee 100644 --- a/make_mozilla/projects/admin.py +++ b/make_mozilla/projects/admin.py @@ -28,6 +28,16 @@ class TagAdmin(admin.ModelAdmin): ) +class GroupAdmin(admin.ModelAdmin): + prepopulated_fields = {'slug': ('name', ), } + + +class GroupMembershipAdmin(admin.ModelAdmin): + list_display = ('project', 'group', 'order', ) + list_editable = ('order', ) + list_filter = ('group', ) + + class TopicAdmin(TagAdmin): pass @@ -45,6 +55,8 @@ class ContributorAdmin(admin.ModelAdmin): admin.site.register(models.Project, ProjectAdmin) +admin.site.register(models.Group, GroupAdmin) +admin.site.register(models.GroupMembership, GroupMembershipAdmin) admin.site.register(models.Topic, TopicAdmin) admin.site.register(models.Difficulty, DifficultyAdmin) admin.site.register(models.Skill, SkillAdmin) diff --git a/make_mozilla/projects/migrations/0004_auto__add_groupmembership__add_group.py b/make_mozilla/projects/migrations/0004_auto__add_groupmembership__add_group.py new file mode 100644 index 0000000..0e3c338 --- /dev/null +++ b/make_mozilla/projects/migrations/0004_auto__add_groupmembership__add_group.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'GroupMembership' + db.create_table('projects_groupmembership', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['projects.Project'])), + ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['projects.Group'])), + ('order', self.gf('django.db.models.fields.IntegerField')(default=1, max_length=2)), + )) + db.send_create_signal('projects', ['GroupMembership']) + + # Adding model 'Group' + db.create_table('projects_group', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('slug', self.gf('django.db.models.fields.SlugField')(default='', unique=True, max_length=50)), + ('body', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('body_text_template', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('take_body_from', self.gf('django.db.models.fields.CharField')(default='body', max_length=4)), + ('image', self.gf('make_mozilla.core.fields.SizedImageField')(max_length=100)), + )) + db.send_create_signal('projects', ['Group']) + + def backwards(self, orm): + # Deleting model 'GroupMembership' + db.delete_table('projects_groupmembership') + + # Deleting model 'Group' + db.delete_table('projects_group') + + models = { + 'events.campaign': { + 'Meta': {'object_name': 'Campaign'}, + 'description': ('django.db.models.fields.TextField', [], {}), + 'end': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'start': ('django.db.models.fields.DateField', [], {}) + }, + 'events.partner': { + 'Meta': {'ordering': "('name',)", 'object_name': 'Partner'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'for_campaign': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['events.Campaign']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200'}) + }, + 'projects.contributor': { + 'Meta': {'object_name': 'Contributor'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'local_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'partner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['events.Partner']", 'null': 'True', 'blank': 'True'}) + }, + 'projects.difficulty': { + 'Meta': {'ordering': "['index', 'label', 'id']", 'object_name': 'Difficulty'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'index': ('django.db.models.fields.IntegerField', [], {'default': '1', 'max_length': '2'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'value': ('django.db.models.fields.SlugField', [], {'max_length': '50'}) + }, + 'projects.group': { + 'Meta': {'object_name': 'Group'}, + 'body': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'body_text_template': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('make_mozilla.core.fields.SizedImageField', [], {'max_length': '100'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['projects.Project']", 'through': "orm['projects.GroupMembership']", 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'default': "''", 'unique': 'True', 'max_length': '50'}), + 'take_body_from': ('django.db.models.fields.CharField', [], {'default': "'body'", 'max_length': '4'}) + }, + 'projects.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'max_length': '2'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'ordering': "['-added']", 'object_name': 'Project'}, + 'added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'body': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'contributor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Contributor']", 'null': 'True', 'blank': 'True'}), + 'difficulties': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['projects.Difficulty']", 'null': 'True', 'blank': 'True'}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('make_mozilla.core.fields.SizedImageField', [], {'max_length': '100'}), + 'link': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'skills': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['projects.Skill']", 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'default': "''", 'unique': 'True', 'max_length': '50'}), + 'teaser': ('django.db.models.fields.TextField', [], {}), + 'tools': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['tools.Tool']", 'null': 'True', 'blank': 'True'}), + 'topics': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['projects.Topic']", 'null': 'True', 'blank': 'True'}), + 'url_hash': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}) + }, + 'projects.projectstep': { + 'Meta': {'object_name': 'ProjectStep'}, + 'content': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'steps'", 'to': "orm['projects.Project']"}) + }, + 'projects.skill': { + 'Meta': {'ordering': "['index', 'label', 'id']", 'object_name': 'Skill'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'index': ('django.db.models.fields.IntegerField', [], {'default': '1', 'max_length': '2'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'value': ('django.db.models.fields.SlugField', [], {'max_length': '50'}) + }, + 'projects.topic': { + 'Meta': {'ordering': "['index', 'label', 'id']", 'object_name': 'Topic'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'index': ('django.db.models.fields.IntegerField', [], {'default': '1', 'max_length': '2'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'value': ('django.db.models.fields.SlugField', [], {'max_length': '50'}) + }, + 'tools.tool': { + 'Meta': {'ordering': "['name']", 'object_name': 'Tool'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'new': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'strapline': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['projects'] \ No newline at end of file diff --git a/make_mozilla/projects/models.py b/make_mozilla/projects/models.py index bee2797..af1fd02 100644 --- a/make_mozilla/projects/models.py +++ b/make_mozilla/projects/models.py @@ -60,6 +60,14 @@ def get_absolute_url(self): return self.link return reverse('project', kwargs={'slug': self.slug}) + @property + def groups(self): + try: + return self.group_set.all() + # because we have the 'dummy project' that doesn't have a primary key + except ValueError: + return None + @property def content(self): if self.steps.count(): @@ -91,6 +99,50 @@ def next(self): return None +class Group(models.Model): + name = models.CharField(max_length=255) + slug = models.SlugField(unique=True, default='', + help_text="Remember if you change this after a group has been saved it can cause broken links") + members = models.ManyToManyField(Project, through='GroupMembership') + body = models.TextField(null=True, blank=True, + help_text='Please paste your HTML in here - please remember to include any images behind an HTTPS server') + body_text_template = models.CharField(max_length=255, blank=True, null=True, + help_text="If supplying an HTML template for the body include the filename here. We look for templates in 'make_mozilla/projects/templates/groups/'") + take_body_from = models.CharField(max_length=4, choices=( + ('body', 'body'), + ('temp', 'body_text_template') + ), + default='body', + help_text='Do you want the introduction text to be taken direct from the DB or from an HTML template file?') + image = fields.SizedImageField( + upload_to='projects', + help_text='Unless custom CSS is written this will appear in the top right of the page, next to the title', + storage=FileSystemStorage(**settings.UPLOADED_IMAGES), + sizes={ + 'poster': 515, + 'flyer': (270, 165), + 'featured': (200, 130), + 'thumb': (126, 77), + }) + + def __unicode__(self): + return self.name + + def get_absolute_url(self): + return reverse('group', kwargs={'slug': self.slug}) + + @property + def projects(self): + return self.members.all().order_by('groupmembership__order') + + +class GroupMembership(models.Model): + project = models.ForeignKey(Project) + group = models.ForeignKey(Group) + order = models.IntegerField(max_length=2, default=1, + help_text="Order can also be set on the listing page once saved") + + class ProjectStep(models.Model): project = models.ForeignKey('Project', related_name='steps') content = models.TextField() @@ -147,10 +199,10 @@ def name(self): def website(self): if self.partner: return self.partner.website - return none + return None @property def logo(self): if self.partner: return self.partner.logo - return None \ No newline at end of file + return None diff --git a/make_mozilla/projects/templates/projects/detail.html b/make_mozilla/projects/templates/projects/detail.html index e6d8fe4..f6fd404 100644 --- a/make_mozilla/projects/templates/projects/detail.html +++ b/make_mozilla/projects/templates/projects/detail.html @@ -29,6 +29,14 @@
Contributed by {{ p.contributor }}
{% endif %} +I like cake
+Contributed by {{ p.contributor }}
{% endif %} +Contributed by {{ project.contributor }}
{% endif %} {% endfor %} diff --git a/make_mozilla/projects/urls.py b/make_mozilla/projects/urls.py index 5600192..b128c74 100644 --- a/make_mozilla/projects/urls.py +++ b/make_mozilla/projects/urls.py @@ -2,6 +2,8 @@ from make_mozilla.projects import views urlpatterns = patterns('', + url(r'^groups/(?P