1. Dmytro Budašný
  2. cmsplugin-faq

Commits

Dmytro Budašný  committed 42d6b23 Merge

Merged cms-2.X with default branch

  • Participants
  • Parent commits 612b243, 8471b1c
  • Branches default

Comments (0)

Files changed (12)

File .hgtags

View file
  • Ignore whitespace
+a818d57fb4864b954e653c82e0a4a2beccca634c cms-2.0.2
+a818d57fb4864b954e653c82e0a4a2beccca634c cms-2.0.2
+7c398b7d510e0bc88554fd6bf5b42724a2a19821 cms-2.0.2
+7c398b7d510e0bc88554fd6bf5b42724a2a19821 cms-2.0.2
+33762b0ececf86eb61d1b874ab8698188c207d4f cms-2.0.2
+33762b0ececf86eb61d1b874ab8698188c207d4f cms-2.0.2
+61ca166aa12c52956afe10cd2a006e9ac91d762f cms-2.0.2

File README.txt

View file
  • Ignore whitespace
 BETA
 
+
 Name: cmsplugin-faq
 Description: duplicate of django-cms2's Text plugin: adds a 'topic' field and link anchors in templates; CMSFaqEntryPlugin creates FAQ entries (questions & answers); CMSFaqListPlugin creates <a> anchor list of FAQ entries, on the same page; CMSFaqEntryLinkPlugin links to specific or random CMSFaqEntries
 Download: http://bitbucket.org/tehfink/cmsplugin-faq/
 
+
 Requirements:
-- django-cms-2.0: 2.0.0
-- django: 1.1.1
+- django-cms-2 = 2.0.2
+- django = 1.1.1
 
-Last tested with:
-- django-cms-2.0: rev b8e65a24b0eb1ea329df003af1bb6c2a0cade5c4
-- django: 1.1.1
 
 Setup
+- this release is only compatible with django-cms 2.0.2
 - make sure requirements are installed and properly working
 - add cmsplugin_faq to python path
 - add 'cmsplugin_faq' to INSTALLED_APPS
 - run 'python manage.py syncdb'
 - add plugins to pages
 
+
 Optional
 - define CMSPLUGIN_FAQLIST_CSS_CHOICES in settings
 - copy cmsplugin_faq/templates/plugins/cmsplugin_faq/ to your project directory
 
+
 Todo:
 - allow CMSFaqListPlugin plugin to be on a different page than CMSFaqEntryPlugin
 - test with TinyMCE (should work)
 - subclass Text plugin when this is possible
-- add migrations (south)
+
+
+NB:
+- if you have CMS_MODERATOR enabled, you will see an issue where the FaqEntryLinkPlugin lists FaqEntries twice; the second listing is apparently an empty item. see: http://groups.google.com/group/django-cms/browse_thread/thread/3bc43ed4eb7c5467/2c4af3741bdbaa2d?lnk=gst&q=cms_moderator#2c4af3741bdbaa2d
 
 
 Examples:
 
+in settings.py:
 CMSPLUGIN_FAQENTRY_CSS_CHOICES = (('1', 'featured'),)
 - adds an optional css class to the faq entry in the plugin template
 
+in a template:
+{% get_latest_faqs 3 as latest_faqs %}
+{% for latest in latest_faqs %}
+...
+{% endfor %}
+- returns latest Faq plugins descendant from the current page
+
 
 Note:
 This is not great code, but it works. Please tell me how to make it better!

File cmsplugin_faq/__init__.py

View file
  • Ignore whitespace
-VERSION = (0, 2, 1, 'beta')
+VERSION = (0, 2, 4, 'beta')
 __version__ = '.'.join(map(str, VERSION))

File cmsplugin_faq/cms_plugins.py

View file
  • Ignore whitespace
         return super(CMSFaqEntryPlugin, self).get_form(request, obj, **kwargs)
 
     def render(self, context, instance, placeholder):
+        from django.template.defaultfilters import slugify
         context.update({
-            'body':plugin_tags_to_user_html(instance.body, context, placeholder),
-            'topic':instance.topic,
-            'placeholder':placeholder,
-            'object':instance,
-            'css' : instance.get_css_display(),
+            'body': plugin_tags_to_user_html(instance.body, context, placeholder),
+            'topic': instance.topic,
+            'name': slugify(instance.topic),
+            'css': instance.get_css_display(),
+            'placeholder': placeholder,
+            'object': instance,
         })
         return context
 
     model = FaqList
     name = _("FAQ List")
     render_template = "plugins/cmsplugin_faq/faq_list.html"
-    
+
     def render(self, context, instance, placeholder):
 
         #get all FaqEntryPlugin on this page and this language
         language = context.get('lang', settings.LANGUAGE_CODE)
         plugins = instance.placeholder.cmsplugin_set.filter(plugin_type='CMSFaqEntryPlugin', language=language)
-        
+
         faqentry_plugins = []
 
         #make a list of the faqentry plugin objects
                 plugin.faqentry.body = ''
             faqentry_plugins.append(plugin.faqentry)
 
-        context.update({'faq_list':faqentry_plugins, 'placeholder':placeholder})
-        context.update({'css' : instance.get_css_display()})
+        context.update({
+            'faq_list': faqentry_plugins,
+            'css': instance.get_css_display(),
+            'placeholder': placeholder,
+        })
+
         return context
 
 plugin_pool.register_plugin(CMSFaqListPlugin)
 
 
+#DOESN'T WORK WITH 2.0.2 & CMS_MODERATOR: published plugin `link` field is blank, so returned link plugin is random
 class CMSFaqEntryLinkPlugin(CMSPluginBase):
-    """Links to a single FaqEntry plugins"""
+    """Links to a single FaqEntry plugin"""
 
     model = FaqEntryLink
     name = _("FAQ Entry Link")
     text_enabled = True
     render_template = "plugins/cmsplugin_faq/faq_entry_link.html"
-    
+
     def render(self, context, instance, placeholder):
 
+#        import ipdb; ipdb.set_trace()
+
         #if a faqentry is not specified, choose one at random
         if not instance.link:
             faqentry_plugins = []
                 instance.link = random.sample(faqentry_plugins, 1)[0]
                 #set the page id of the linked faqentry
                 page_id = instance.link.page_id
-            except (ValueError, AttributeError), e:
+            except (ValueError, AttributeError):                    #since this didn't work, we assume no plugin exists
                 raise ValueError("No FaqEntryPlugin was returned. Make sure one exists and is published.")
-                
+
         #truncate the entry's body
         if instance.truncate_body and instance.link.body:
             instance.link.body = truncate_words(instance.link.body, instance.truncate_body)
-            
+
         #show the entry's body or not
         if not instance.show_body:
             instance.link.body = ''
 
-        #create the link URL
-        from cms.models import Page
-        #if page_id was not set randomly
-        try:
-            page_id
-        except (NameError, UnboundLocalError):
-            page_id = instance.faqentrylink.link.page_id
-#        import pdb; pdb.set_trace()
-        url = '/' + instance.link.language + Page.objects.get(id=page_id).get_absolute_url()
-        
         context.update({
             'body':plugin_tags_to_user_html(instance.link.body, context, placeholder),
             'topic':instance.link.topic,
-            'url': url,
+            'url': instance.link.get_absolute_url(),
             'placeholder':placeholder,
             'object':instance,
             'css' : instance.get_css_display(),

File cmsplugin_faq/migrations/0001_initial.py

View file
  • Ignore whitespace
+# encoding: 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 'FaqEntry'
+        db.create_table('cmsplugin_faqentry', (
+            ('topic', self.gf('django.db.models.fields.CharField')(max_length=500)),
+            ('body', self.gf('django.db.models.fields.TextField')()),
+            ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
+            ('css', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True)),
+        ))
+        db.send_create_signal('cmsplugin_faq', ['FaqEntry'])
+
+        # Adding model 'FaqList'
+        db.create_table('cmsplugin_faqlist', (
+            ('truncate_body', self.gf('django.db.models.fields.PositiveSmallIntegerField')(default=5)),
+            ('show_body', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
+            ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
+            ('css', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True)),
+        ))
+        db.send_create_signal('cmsplugin_faq', ['FaqList'])
+
+        # Adding model 'FaqEntryLink'
+        db.create_table('cmsplugin_faqentrylink', (
+            ('truncate_body', self.gf('django.db.models.fields.PositiveSmallIntegerField')(default=5)),
+            ('link', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['cmsplugin_faq.FaqEntry'], null=True, blank=True)),
+            ('show_body', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
+            ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
+            ('css', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True)),
+        ))
+        db.send_create_signal('cmsplugin_faq', ['FaqEntryLink'])
+    
+    
+    def backwards(self, orm):
+        
+        # Deleting model 'FaqEntry'
+        db.delete_table('cmsplugin_faqentry')
+
+        # Deleting model 'FaqList'
+        db.delete_table('cmsplugin_faqlist')
+
+        # Deleting model 'FaqEntryLink'
+        db.delete_table('cmsplugin_faqentrylink')
+    
+    
+    models = {
+        'cms.cmsplugin': {
+            'Meta': {'object_name': 'CMSPlugin'},
+            'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language': ('django.db.models.fields.CharField', [], {'max_length': '5', 'db_index': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'page': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Page']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
+            'placeholder': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+            'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+            'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.CMSPlugin']"}),
+            'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+        },
+        'cms.page': {
+            'Meta': {'object_name': 'Page'},
+            'changed_by': ('django.db.models.fields.CharField', [], {'max_length': '70'}),
+            'created_by': ('django.db.models.fields.CharField', [], {'max_length': '70'}),
+            'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'in_navigation': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'menu_login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'moderator_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '1', 'blank': 'True'}),
+            'navigation_extenders': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['cms.Page']"}),
+            'publication_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'publication_end_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'published': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.Page']"}),
+            'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'reverse_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '40', 'null': 'True', 'blank': 'True'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}),
+            'soft_root': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
+            'template': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+        },
+        'cmsplugin_faq.faqentry': {
+            'Meta': {'object_name': 'FaqEntry', 'db_table': "'cmsplugin_faqentry'", '_ormbases': ['cms.CMSPlugin']},
+            'body': ('django.db.models.fields.TextField', [], {}),
+            'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+            'css': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'topic': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+        },
+        'cmsplugin_faq.faqentrylink': {
+            'Meta': {'object_name': 'FaqEntryLink', 'db_table': "'cmsplugin_faqentrylink'", '_ormbases': ['cms.CMSPlugin']},
+            'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+            'css': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'link': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cmsplugin_faq.FaqEntry']", 'null': 'True', 'blank': 'True'}),
+            'show_body': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'truncate_body': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '5'})
+        },
+        'cmsplugin_faq.faqlist': {
+            'Meta': {'object_name': 'FaqList', 'db_table': "'cmsplugin_faqlist'", '_ormbases': ['cms.CMSPlugin']},
+            'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+            'css': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'show_body': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'truncate_body': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '5'})
+        },
+        'sites.site': {
+            'Meta': {'object_name': 'Site', 'db_table': "'django_site'"},
+            'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        }
+    }
+    
+    complete_apps = ['cmsplugin_faq']

File cmsplugin_faq/migrations/__init__.py

  • Ignore whitespace
Empty file added.

File cmsplugin_faq/models.py

View file
  • Ignore whitespace
     topic = models.CharField(_("Topic"),max_length=500, help_text=_('FAQ entry topic'))
     css = models.CharField(_('CSS class'), max_length=1, choices=CMSPLUGIN_FAQENTRY_CSS_CHOICES, blank=True, help_text=_('Additional CSS class to apply'))
     body = models.TextField(_("body"))
-   
+
     def _set_body_admin(self, text):
         self.body = plugin_admin_html_to_tags(text)
 
 
     search_fields = ('topic', 'body',)
 
+    def get_absolute_url(self):
+        """ returns url pointing to the anchor in the cms Page containing the FaqEntry plugin """
+
+        #use django's slugify for the href anchor (e.g.: remove non-ASCII characters)
+        from django.template.defaultfilters import slugify
+
+        #create the FaqEntry's url as a combination of the Page's url + '#' + the slugified anchor
+        url = "%s#%s" % (self.page.get_absolute_url(language=self.language, fallback=False), slugify(self.topic))
+
+#        import ipdb; ipdb.set_trace()
+
+        #supposedly the following is not necessary. but i haven't been able to get it working with multilingual otherwise
+        #check if multilingual middleware is installed
+        if 'cms.middleware.multilingual.MultilingualURLMiddleware' in settings.MIDDLEWARE_CLASSES:
+            #prepend language namespace (better way to do this?)
+            url = '/' + self.language + url
+
+        return url
+
     def __unicode__(self):
         return u"%s" % (truncate_words(self.topic, 5)[:30]+"...")
 
     def __unicode__(self):
         return u"%s" % (self.page.get_page_title())
 
+
 #get custom css from settings or use default
 CMSPLUGIN_FAQENTRYLINK_CSS_CHOICES = getattr(settings,"CMSPLUGIN_FAQENTRYLINK_CSS_CHOICES", (('1', 'faq-entry-link-small'),) )
 
 class FaqEntryLink(CMSPlugin):
     """Model to give FaqEntryLink plugin various options"""
-    link = models.ForeignKey(FaqEntry, blank=True, null=True, verbose_name=_('Linked FAQ Entry'), help_text=_('Leave empty for random'))
+    link = models.ForeignKey(FaqEntry, limit_choices_to={'publisher_is_draft': False}, blank=True, null=True, verbose_name=_('Linked FAQ Entry'), help_text=_('Leave empty for random'))
     truncate_body = models.PositiveSmallIntegerField(_('Truncate words'), default=5, help_text=_('Truncate FAQ Entry body by this many words; zero means Django default'))
     show_body = models.BooleanField(_('Show FAQ Entry body'),default=True)
     css = models.CharField(_('CSS class'), max_length=1, choices=CMSPLUGIN_FAQENTRYLINK_CSS_CHOICES, blank=True, help_text=_('Additional CSS class to apply'))

File cmsplugin_faq/templates/plugins/cmsplugin_faq/faq_entry.html

View file
  • Ignore whitespace
 
 <div class="faq-entry{% if css %} {{ css }}{% endif %}">
-    <div><a name="{{ topic|safe }}">{{ topic|safe }}</a></div>
-    <div>
+
+    <div class="faq-entry-topic">
+        <a name="{{ name }}">
+            {{ topic|safe }}
+        </a>
+    </div>
+
+    <div class="faq-entry-body">
         {{ body|safe }}
     </div>
+
 </div>

File cmsplugin_faq/templates/plugins/cmsplugin_faq/faq_entry_link.html

View file
  • Ignore whitespace
 
 <div class="faq-entry-link{% if css %} {{ css }}{% endif %}">
-    <div><a href="{{ url }}#{{ topic|safe }}">{{ topic|safe }}</a></div>
+
+    <div class="faq-entry-topic">
+        <a href="{{ url }}">
+            {{ topic|safe }}
+        </a>
+    </div>
+
     {% if body %}
-    <div>
+    <div class="faq-entry-body">
         {{ body|safe }}
     </div>
     {% endif %}
+
 </div>

File cmsplugin_faq/templates/plugins/cmsplugin_faq/faq_list.html

View file
  • Ignore whitespace
 
 <div class="faq-list">
+
     <ul>
 {% for faq_entry in faq_list %}
-        <li{% if css %} class="{{ css }}"{% endif %}><a href="#{{ faq_entry.topic|safe }}">{{ faq_entry.topic|safe }}</a>
-        <p>{{ faq_entry.body|safe|truncatewords_html:10 }}
+        <li{% if css %} class="{{ css }}"{% endif %}>
+
+            <div class="faq-entry-topic">
+                <a href="#{{ faq_entry.topic|safe }}">
+                    {{ faq_entry.topic|safe }}
+                </a>
+            <div>
+
+            <div class="faq-entry-body">
+                {{ faq_entry.body|safe|truncatewords_html:10 }}
+            <div>
+
         <hr>
 {% endfor %}
     </ul>
+
 </div>

File cmsplugin_faq/templatetags/__init__.py

  • Ignore whitespace
Empty file added.

File cmsplugin_faq/templatetags/cmsplugin_faq_templatetags.py

View file
  • Ignore whitespace
+from django import template
+from cmsplugin_faq.models import FaqEntry
+from django.core.cache import cache
+register = template.Library()
+
+
+
+class LatestFAQsNode(template.Node):
+    """
+    parses & checks arguments passed to ``get_latest_faqs``; returns applicable Queryset of recently added CMSFaqPlugin models descendant from and including the current page
+    """
+
+    def __init__(self, num, varname):
+        num, varname
+
+        #'All' means slicing with [:None] , which returns everything
+        if num == 'All' or num == 'all':
+            num = None
+        else:
+            num = abs(int(num))
+        self.num = num
+
+        self.varname = varname
+
+    def render(self, context):
+
+        #shortcircuit for django admin
+        if context.has_key('current_page'):
+            page = context['current_page']
+
+            if page is 'dummy':
+                return ''
+
+            #check cache first
+            if cache.get('cmsplugin_faq_templatetags_get_latest_faqs'):                                     #if latest_pages exists in cache, return it immediately
+                 context[self.varname] = cache.get('cmsplugin_faq_templatetags_get_latest_faqs')
+            else:
+                #apparently publisher_is_draft has different meanings depending on the status of CMS_MODERATOR?
+                from django.conf import settings
+                if settings.CMS_MODERATOR:
+                    #this seems logical
+                    PUBLISHER_STATE = False
+                else:
+                    #this does not seem logical
+                    PUBLISHER_STATE = True
+
+                #get published descendant Pages of the current Page
+                subpages = page.get_descendants().filter(publisher_is_draft=PUBLISHER_STATE)
+
+                #list of all published faq plugins for descendant and current page
+                allfaqs= []
+
+                #get published plugins for this page
+                for faq in page.cmsplugin_set.filter(plugin_type='CMSFaqEntryPlugin', publisher_is_draft=PUBLISHER_STATE):
+                    allfaqs.append(faq)
+
+                #get published plugins for each subpage
+                for subpage in subpages:
+                    for subpagefaq in subpage.cmsplugin_set.filter(plugin_type='CMSFaqEntryPlugin', publisher_is_draft=PUBLISHER_STATE):
+                        allfaqs.append(subpagefaq)
+
+                #shortened list according to given argument
+                #putting this here for performance reasons?
+                context[self.varname] = allfaqs[:self.num]
+
+                cache.set('cmsplugin_faq_templatetags_get_latest_faqs', context[self.varname])               #add latest_pages to the django cache
+
+        return ''
+
+@register.tag('get_latest_faqs')
+def get_latest_faqs(parser, token):
+    """
+    A django-cms templatetag for returning recently added CMSFaqPlugin models.
+    Note that the tag only returns Faq plugins descendant from the current page.
+
+    Some common case examples::
+
+        {% get_latest_faqs All as latest_faqs %}
+        {% for latest in latest_faqs %}
+            ...
+        {% endfor %}
+
+        {% get_latest_faqs 3 as latest_faqs %}
+        {% for latest in latest_faqs %}
+            ...
+        {% endfor %}
+
+    Supported arguments are: ``All``, ``all``, positive integers, and zero
+    """
+
+    #split up arguments
+    bits = token.split_contents()
+
+    #knock off the first argument
+    bits.pop(0)
+    num = bits[0]
+    varname = bits[2]
+
+    if len(bits) != 3:
+        raise template.TemplateSyntaxError, "get_latest tag takes exactly three arguments"
+    if bits[1] != 'as':
+        raise template.TemplateSyntaxError, "second argument to get_latest tag must be 'as'"
+    return LatestFAQsNode(num, varname)