diff --git a/README.md b/README.md index b34290e..975f077 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,21 @@ Aldryn Segmentation =================== -Development Status ------------------- - -### Segment Limter: -- [x] Limiter - -### Segment Plugins: -- [x] Fallback -- [x] Segment by Switch (hard switch) -- [x] Segment by Cookie -- [x] Segment by Country -- [x] Segment by Auth Status (is authenticated) -- [x] Overridable - -### Segment Pool: -- [x] Registration -- [x] De-registration -- [x] Discovery -- [x] Get override state for segment -- [x] Reset all overrides -- [x] Supports multiple operators - -### Toolbar: -- [x] Displays -- [x] Working override states -- [x] Working "Reset all" option -- [x] Displays no. of overrides in Segment menu name - -### Tests: - -These tests are performed in the test_project. - -- [x] Segment logic tests -- [x] Segment Pool tests -- [x] I18N tests -- [x] Multiple operator tests - -### Other: -- [x] Move the Country Segment and its related bits to another repo? -- [x] Ensure compatibility with supported Pythons (vs. Django 1.6) - - [x] Python 2.6.x - - [x] Python 2.7.x - - [x] Python 3.3.x - - [x] Python 3.4.x -- [x] Ensure compatibility with supported Djangos (vs. Python 2.7) - - [x] Django 1.4.x - - [x] Django 1.5.x - - [x] Django 1.6.x - - -Known Issues ------------- - -This is a list of known issues that are being worked on... - -- [ ] UI for the Limit plugin needs some work. Ideas? - -- [x] French translation is available (via by translate.google.com) - -- [x] Does not seem to work with the new Alias Plugin... - Actually, its just that the AliasPlugin didn't work in Django 1.4.13. - I've submitted a PR to fix this and added tests to help detect and - prevent future issues. - - Installation ------------ At this time, the package is not submitted to PyPi, but you can still use pip if you like. Here's how to get started quickly: -NOTE: At this time, the project has only been tested under: -- Python 2.6, 2.7, 3.3, 3.4 -- Django 1.4, 1.5, 1.6 -- django CMS 3.0.2 (276fd37b0e49555bafce6c071ca50508de5e4c49 or later) +Requirements: +- python 2.7+ or 3.5+ +- Django 1.8+ +- django CMS 3.5+ -1. Make sure you're using a version of django-CMS that is later than - 3.0.2.dev1, otherwise the Segment menu will not appear correctly and likely - the whole toolbar won't render at all. 1. If you're using Python 3, it is recommended also install: `pip intall pyuca` for better collation of non-EN languages. 1. `pip install https://github.com/aldryn/aldryn-segmentation/archive/master.zip` 1. Add 'aldryn_segmentation' to INSTALLED_APPS in your Django project's settings file -1. `python manage.py schemamigration aldryn_segmentation --initial` 1. `python manage.py migrate aldryn_segmentation` Optional, but required if you intend to run the test project included in the diff --git a/addon.json b/addon.json index 2281a86..1dcb457 100644 --- a/addon.json +++ b/addon.json @@ -1,16 +1,4 @@ { - "name": "Aldryn Segmentation", - "description": "Provides market segmentation to Aldryn!", - "url": "https://github.com/aldryn/aldryn-segmentation", "package-name": "aldryn-segmentation", - "installed-apps": [ - "aldryn_segmentation" - ], - "author": { - "name": "Divio", - "url": "https://www.aldryn.com" - }, - "license": { - "name": "BSD" - } -} \ No newline at end of file + "installed-apps": [] +} diff --git a/aldryn_config.py b/aldryn_config.py new file mode 100644 index 0000000..246c720 --- /dev/null +++ b/aldryn_config.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +from aldryn_client import forms + + +class Form(forms.BaseForm): + + def to_settings(self, data, settings): + settings['INSTALLED_APPS'].extend([ + 'aldryn_segmentation', + ]) + return settings diff --git a/aldryn_segmentation/cms_plugins/segment_limiter.py b/aldryn_segmentation/cms_plugins/segment_limiter.py index 91313f8..ce2e39c 100644 --- a/aldryn_segmentation/cms_plugins/segment_limiter.py +++ b/aldryn_segmentation/cms_plugins/segment_limiter.py @@ -31,7 +31,6 @@ def render(self, context, instance, placeholder): context, instance) return context - def is_context_appropriate(self, context, instance): ''' Returns True if any of its children are context-appropriate, @@ -41,7 +40,6 @@ def is_context_appropriate(self, context, instance): num_apt = sum( 1 for child in apt_children if child[1] ) return num_apt > 0 - def get_context_appropriate_children(self, context, instance): from ..segment_pool import SegmentOverride ''' diff --git a/aldryn_segmentation/cms_toolbar.py b/aldryn_segmentation/cms_toolbars.py similarity index 100% rename from aldryn_segmentation/cms_toolbar.py rename to aldryn_segmentation/cms_toolbars.py diff --git a/aldryn_segmentation/migrations/0002_auto_20170823_0446.py b/aldryn_segmentation/migrations/0002_auto_20170823_0446.py new file mode 100644 index 0000000..24f6ca7 --- /dev/null +++ b/aldryn_segmentation/migrations/0002_auto_20170823_0446.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('aldryn_segmentation', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='authenticatedsegmentpluginmodel', + name='cmsplugin_ptr', + field=models.OneToOneField(parent_link=True, related_name='aldryn_segmentation_authenticatedsegmentpluginmodel', auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin'), + ), + migrations.AlterField( + model_name='cookiesegmentpluginmodel', + name='cmsplugin_ptr', + field=models.OneToOneField(parent_link=True, related_name='aldryn_segmentation_cookiesegmentpluginmodel', auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin'), + ), + migrations.AlterField( + model_name='fallbacksegmentpluginmodel', + name='cmsplugin_ptr', + field=models.OneToOneField(parent_link=True, related_name='aldryn_segmentation_fallbacksegmentpluginmodel', auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin'), + ), + migrations.AlterField( + model_name='segmentlimitpluginmodel', + name='cmsplugin_ptr', + field=models.OneToOneField(parent_link=True, related_name='aldryn_segmentation_segmentlimitpluginmodel', auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin'), + ), + migrations.AlterField( + model_name='switchsegmentpluginmodel', + name='cmsplugin_ptr', + field=models.OneToOneField(parent_link=True, related_name='aldryn_segmentation_switchsegmentpluginmodel', auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin'), + ), + ] diff --git a/aldryn_segmentation/south_migrations/0001_initial.py b/aldryn_segmentation/south_migrations/0001_initial.py deleted file mode 100644 index 1c2b57b..0000000 --- a/aldryn_segmentation/south_migrations/0001_initial.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- -from south.utils import datetime_utils as 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 'SegmentLimitPluginModel' - db.create_table(u'aldryn_segmentation_segmentlimitpluginmodel', ( - (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), - ('label', self.gf('django.db.models.fields.CharField')(default=u'', max_length=128, blank=True)), - ('max_children', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)), - )) - db.send_create_signal(u'aldryn_segmentation', ['SegmentLimitPluginModel']) - - # Adding model 'FallbackSegmentPluginModel' - db.create_table(u'aldryn_segmentation_fallbacksegmentpluginmodel', ( - (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), - ('label', self.gf('django.db.models.fields.CharField')(default=u'', max_length=128, blank=True)), - )) - db.send_create_signal(u'aldryn_segmentation', ['FallbackSegmentPluginModel']) - - # Adding model 'SwitchSegmentPluginModel' - db.create_table(u'aldryn_segmentation_switchsegmentpluginmodel', ( - (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), - ('label', self.gf('django.db.models.fields.CharField')(default=u'', max_length=128, blank=True)), - ('on_off', self.gf('django.db.models.fields.BooleanField')(default=True)), - )) - db.send_create_signal(u'aldryn_segmentation', ['SwitchSegmentPluginModel']) - - # Adding model 'CookieSegmentPluginModel' - db.create_table(u'aldryn_segmentation_cookiesegmentpluginmodel', ( - (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), - ('label', self.gf('django.db.models.fields.CharField')(default=u'', max_length=128, blank=True)), - ('cookie_key', self.gf('django.db.models.fields.CharField')(default=u'', max_length=4096)), - ('cookie_value', self.gf('django.db.models.fields.CharField')(default=u'', max_length=4096)), - )) - db.send_create_signal(u'aldryn_segmentation', ['CookieSegmentPluginModel']) - - # Adding model 'AuthenticatedSegmentPluginModel' - db.create_table(u'aldryn_segmentation_authenticatedsegmentpluginmodel', ( - (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), - ('label', self.gf('django.db.models.fields.CharField')(default=u'', max_length=128, blank=True)), - )) - db.send_create_signal(u'aldryn_segmentation', ['AuthenticatedSegmentPluginModel']) - - - def backwards(self, orm): - # Deleting model 'SegmentLimitPluginModel' - db.delete_table(u'aldryn_segmentation_segmentlimitpluginmodel') - - # Deleting model 'FallbackSegmentPluginModel' - db.delete_table(u'aldryn_segmentation_fallbacksegmentpluginmodel') - - # Deleting model 'SwitchSegmentPluginModel' - db.delete_table(u'aldryn_segmentation_switchsegmentpluginmodel') - - # Deleting model 'CookieSegmentPluginModel' - db.delete_table(u'aldryn_segmentation_cookiesegmentpluginmodel') - - # Deleting model 'AuthenticatedSegmentPluginModel' - db.delete_table(u'aldryn_segmentation_authenticatedsegmentpluginmodel') - - - models = { - u'aldryn_segmentation.authenticatedsegmentpluginmodel': { - 'Meta': {'object_name': 'AuthenticatedSegmentPluginModel'}, - u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '128', 'blank': 'True'}) - }, - u'aldryn_segmentation.cookiesegmentpluginmodel': { - 'Meta': {'object_name': 'CookieSegmentPluginModel'}, - u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), - 'cookie_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '4096'}), - 'cookie_value': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '4096'}), - 'label': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '128', 'blank': 'True'}) - }, - u'aldryn_segmentation.fallbacksegmentpluginmodel': { - 'Meta': {'object_name': 'FallbackSegmentPluginModel'}, - u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '128', 'blank': 'True'}) - }, - u'aldryn_segmentation.segment': { - 'Meta': {'object_name': 'Segment', 'managed': 'False'}, - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - u'aldryn_segmentation.segmentlimitpluginmodel': { - 'Meta': {'object_name': 'SegmentLimitPluginModel', '_ormbases': ['cms.CMSPlugin']}, - u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '128', 'blank': 'True'}), - 'max_children': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}) - }, - u'aldryn_segmentation.switchsegmentpluginmodel': { - 'Meta': {'object_name': 'SwitchSegmentPluginModel'}, - u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '128', 'blank': 'True'}), - 'on_off': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) - }, - 'cms.cmsplugin': { - 'Meta': {'object_name': 'CMSPlugin'}, - 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), - 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), - 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), - 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), - 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}) - }, - 'cms.placeholder': { - 'Meta': {'object_name': 'Placeholder'}, - 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) - } - } - - complete_apps = ['aldryn_segmentation'] \ No newline at end of file diff --git a/aldryn_segmentation/south_migrations/__init__.py b/aldryn_segmentation/south_migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/aldryn_segmentation/templatetags/segmentation_tags.py b/aldryn_segmentation/templatetags/segmentation_tags.py index b58942e..59947cc 100644 --- a/aldryn_segmentation/templatetags/segmentation_tags.py +++ b/aldryn_segmentation/templatetags/segmentation_tags.py @@ -2,66 +2,41 @@ from __future__ import unicode_literals +from cms.toolbar.utils import get_toolbar_from_request from django import template -from classytags.core import Options -from classytags.arguments import Argument - -from cms.templatetags.cms_tags import RenderPlugin - -from ..segment_pool import SegmentOverride +from cms.templatetags import cms_tags register = template.Library() -class RenderSegmentPlugin(RenderPlugin): - name = 'render_segment_plugin' - options = Options( - Argument('plugin'), - Argument('render_plugin') - ) - - def is_renderable(self, context, plugin_instance): - ''' - Determines whether this plugin is to be rendered in this context. - ''' - - # TODO: Seems to me this method is not necessary - # Once tests are established then maybe we can remove this. - - child_plugin = plugin_instance.get_plugin_class_instance() - - try: - allow_overrides = child_plugin.allow_overrides - get_override = child_plugin.get_segment_override - except AttributeError: - # This doesn't quack like a SegmentPlugin, so, it always renders. - return True - - # This is a segment plugin... or at least quacks like one. - if allow_overrides: - # only evaluate override if we allow overrides - override = get_override(context, plugin_instance) - - if override == SegmentOverride.ForcedActive: - return True - elif override == SegmentOverride.ForcedInactive: - return False - - # OK, what does the plugin's is_context_appropriate() say? - return child_plugin.is_context_appropriate(context, plugin_instance) - - def render_tag(self, context, plugin, render_plugin): - response = super(RenderSegmentPlugin, self).render_tag(context, plugin) - - if not (render_plugin and self.is_renderable(context, plugin)): - # OK, this is a Segmentation Plugin that is NOT appropriate for - # rendering in the current context. Unfortunately, we need to - # render the plugin, but throw away the results in order to allow - # the structureboard to display properly. Ugh! +@register.simple_tag(takes_context=True) +def render_segment_plugin(context, plugin, render_plugin): + request = context['request'] + toolbar = get_toolbar_from_request(request) + if toolbar.uses_legacy_structure_mode: + # We need to handle it the old way. Render ALL plugins on the page so + # that the legacy structure board can display all of them. + rendered = cms_tags.render_plugin(context, plugin) + if render_plugin: + return rendered + else: + # We had to render the plugin and all its sub-plugins above and + # so that the legacy structure board sees the invisible plugins. + # (js via sekizai). + # We return nothing as content though, since the plugin should not + # be visible. + return '' + else: + # The new way. yay! + # The structure including all the hidden plugins is handled + # entirely separate. So we don't have to worry about it here. + # We only need to render plugins that are actually visible. + if render_plugin: + return cms_tags.render_plugin(context, plugin) + else: + # FIXME: hmmm... why does the popup not work if the plugin is not + # rendered? It should work without it. + cms_tags.render_plugin(context, plugin) return '' - return response - - -register.tag(RenderSegmentPlugin) diff --git a/schemamigration.py b/schemamigration.py deleted file mode 100644 index 2eed1e2..0000000 --- a/schemamigration.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import sys - -INSTALLED_APPS = [ - 'django.contrib.contenttypes', - 'django.contrib.auth', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.admin', - 'mptt', - 'cms', - 'menus', - 'south', - 'aldryn_segmentation', -] - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:', - } -} - -TEMPLATE_CONTEXT_PROCESSORS = [ - 'django.core.context_processors.auth', - 'django.core.context_processors.i18n', - 'django.core.context_processors.request', - 'django.core.context_processors.media', - 'django.core.context_processors.static', - 'cms.context_processors.media', - 'sekizai.context_processors.sekizai', -] - -ROOT_URLCONF = 'cms.urls' - -def schemamigration(): - - # - # turn ``schemamigration.py --initial`` into: - # ``manage.py schemamigration aldryn_segmentation --initial`` - # and setup the environment. - # - - from django.conf import settings - - from django.core.management import ManagementUtility - settings.configure( - INSTALLED_APPS=INSTALLED_APPS, - ROOT_URLCONF=ROOT_URLCONF, - DATABASES=DATABASES, - TEMPLATE_CONTEXT_PROCESSORS=TEMPLATE_CONTEXT_PROCESSORS - ) - argv = list(sys.argv) - argv.insert(1, 'schemamigration') - argv.insert(2, 'aldryn_segmentation') - utility = ManagementUtility(argv) - utility.execute() - -if __name__ == "__main__": - schemamigration() \ No newline at end of file