Beispiel #1
0
 def inner(parent_id, latest, kind, tag, values, kw):
     if kind is not None:
         kind = kind.lower().split(',')
     
     if values is None: values = 'name', 'title', 'description', 'path'
     else: values = values.split(',')
     
     for i in values:
         if i not in ('name', 'title', 'description', 'path', 'kind', 'rendered', 'tags', 'children', 'descendants'):
             return 'json:', dict(status="error", message="Forbidden.")
     
     children = []
     
     def criteria(child):
         if kind is not None and child.__class__.__name__.lower() not in kind:
             return False
         
         if tag is not None:
             if tag[0] != '!' and tag not in child.tags:
                 return False
             
             elif tag[0] == '!' and tag in child.tags:
                 return False
         
         if not child.controller.allowed:
             return False
         
         return True
     
     try:
         for child in parent.children:
             if not criteria(child):
                 continue
             
             data = dict()
             
             for value in values:
                 if value == 'kind':
                     data[value] = child.__class__.__name__.lower()
                     continue
                 
                 if value == 'children':
                     # We won't return the actual child objects… just if we have children or not.
                     data[value] = bool(len(Asset.objects(parent=child).only('id')))
                     continue
                 
                 if value == 'descendants':
                     # Match child objects that also meet this search criteria.
                     data[value] = bool([i for i in Asset.objects(parent=child).only('acl', 'parent', 'parents', 'tags') if criteria(i)])
                     continue
                 
                 data[value] = getattr(child, value, '')
             
             children.append(data)
     
     except:
         log.exception("Error iterating children.")
         return 'json:', dict(status="error", message="Error determining asset contents.")
     
     return 'json:', dict(status="success", children=children)
Beispiel #2
0
 def __lookup__(self, *remainder, **kw):
     from web.extras.contentment.components.asset.model import Asset
     
     asset = self.asset
     
     if not remainder:
         return self, [asset.default]
     
     remainder = list(remainder)
     
     if asset.path == '/' and remainder == ['sitemap.xml']:
         return self, ['sitemap_xml']
     
     if web.core.request.script_name == '':
         # Path-based lookup, used only when starting from the site root.
         
         paths = []
         
         for i in xrange(1, len(remainder) + (0 if ':' in remainder[-1] else 1)):
             paths.append('/' + '/'.join(remainder[:i]))
         
         nearest = Asset.objects(path__in=paths).order_by('-path').first()
         
         if nearest:
             consumed = paths.index(nearest.path) + 1
             
             remainder = remainder[consumed:]
             for i in range(consumed):
                 web.core.request.path_info_pop()
             
             return nearest.controller, remainder if remainder else [nearest.default]
     
     # Attribute-based lookup.
     
     node = remainder.pop(0).replace('%3A', ':')
     
     log.debug("Looking in %r for %r *%r...", self, node, remainder)
     
     if ":" in node:
         return self, [node.replace(":", "_")] + list(remainder)
     
     if isinstance(node, basestring):
         record = Asset.objects(name=node, parent=asset).first()
         
         if not record:
             raise http.HTTPNotFound("Unable to find resource at this location.")
         
         web.core.request.path_info_pop()
         
         return record.controller, remainder if remainder else [record.default]
     
     raise http.HTTPNotFound("Unable to find resource at this location.")
Beispiel #3
0
def iter_templates():
    from web.extras.contentment.components.asset.model import Asset
    
    container = Asset.objects(path='/settings/templates/custom').first()
    assert container
    
    yield ':none', "No Template"
    yield '', "Default Template"
    
    custom = Asset.objects(parent=container).order_by('title')
    
    if len(custom):
        yield "Custom Templates", [(template.path, template.title) for template in custom]
Beispiel #4
0
 def native(self, value):
     from web.extras.contentment.components.asset.model import Asset
     
     value = super(AssetPathTransform, self).native(value)
     if value is None: return None
     
     return Asset.objects(path=value).first()
Beispiel #5
0
 def inner(self, *args, **kw):
     if not authorized(self.asset):
         raise web.core.http.HTTPUnauthorized
     
     template = "json:"
     data = f(self, *args, **kw)
     
     if not isinstance(data, tuple):
         return data
     
     template, data = data
     
     if data is None:
         data = dict()
     
     root = data['root'] = Asset.objects(path='/').first()
     asset = data['asset'] = self.asset
     
     if not asset.template:
         data['template'] = root.properties.get('org-contentment-default-template', root.properties['org-contentment-theme'] + '.templates.master')
     
     elif asset.template == ':none':
         data['template'] = root.properties['org-contentment-theme'] + '.templates.master'
     
     if data.get('template', asset.template)[0] == '/':
         template_ = Asset.objects(path=data.get('template', asset.template)).first()
         
         if template_:
             fh = tempfile.NamedTemporaryFile(mode='wb', prefix='contentment-template-', suffix='.html', delete=False)
             fh.write('<%inherit file="' + root.properties['org-contentment-theme'] + '.templates.master"/>\n' + template_.content)
             fh.close()
             
             data['template'] = fh.name
         
         else:
             data['template'] = root.properties['org-contentment-theme'] + '.templates.master'
     
     base = '.'.join(f.__module__.split('.')[:-1]) + '.templates.'
     
     # TODO: Allow overriding of templates by the theme.
     
     return 'mako:' + base + template, data
Beispiel #6
0
 def asset(self):
     from web.extras.contentment.components.asset.model import Asset
     
     identifier = self._identifier
     
     try:
         if isinstance(identifier, Asset): return identifier
         elif identifier is not None: return Asset.objects.with_id(identifier)
         
         return Asset.objects(path='/').first()
     
     except:
         log.exception("Error loading model instance for %r instance using %r.", self.__class__.__name__, identifier)
         raise http.HTTPNotFound("Unable to find resource at this location.")
Beispiel #7
0
 def bulk(self, upload):
   f = File()
   
   f.title, _, _ = upload.filename.rpartition('.')
   f.content = upload.file
   f.content.content_type = f.mimetype = upload.type
   f.content.filename = f.filename = upload.filename
   f.size = upload.length
   
   
   siblings = [i.name for i in Asset.objects(parent=self.controller.asset).only('name')]
   f.name = normalize(f.title.lower(), siblings)
   
   f.save()
   f.attach(self.controller.asset)
   
   return 'json:', dict()
Beispiel #8
0
 def cache(name, date):
     from web.extras.contentment.components.page import engines
     
     engine = engines[self.engine]
     content = engine(self.content)
     
     # Determine substitutions and associated asset dates.
     replacements = []
     
     if engine.replacements:
         find = self.content.find
         index = find('${')
         while index != -1:
             newline = find('\n', index)
             close = find('}', index)
             
             if close == -1 or (newline != -1 and newline < close):
                 return u"""<div class="error">Error parsing includes.</div>"""
             
             replacements.append(self.content[index:close+1])
             
             index = find('${', close)
     
     if replacements:
         from web.extras.contentment.components.asset.model import Asset
         
         for replacement in replacements:
             path, _, args = replacement[2:-1].partition(' ')
             embedded = Asset.objects(path=path).first()
             
             if not embedded:
                 content = content.replace(replacement, u"""<span class="error">Error looking up embedded asset with path <tt>%s</tt>.</span>""" % (path, ))
             
             # TODO: There has to be a better way to do this that accounts for quoted strings.
             args = dict([(i.split('=')[0], i.split('=')[1]) for i in args.split()])
             
             try:
                 # embedded = u'''<div class="embed" id="%s-embed">%s</div>''' % (embedded.name, unicode(embedded.embed(**args)))
                 content = content.replace(replacement, unicode(embedded.embed(**args)))
             
             except:
                 log.exception("Error while embedding.")
                 content = content.replace(replacement, u"""<span class="error">Error embedding asset with path <tt>%s</tt>.</span>""" % (path, ))
     
     return content
Beispiel #9
0
 def native(self, value):
     from web.extras.contentment.components.asset.model import Asset
     
     value = super(AssetListTransform, self).native(value)
     if value is None: return []
     
     result = []
     
     try:
         for i in value.split(','):
             i = i.strip()
             result.append(Asset.objects(path=i).first())
     
     except:
         log.exception("Error.")
         raise TransformException()
     
     return result
Beispiel #10
0
 def do(asset):
     try:
         asset = Asset.objects(id=asset).first()
         if not asset:
             return ''
     
     except:
         return num
     
     if hasattr(asset, 'content') and asset.content is None:
         print("missing content... ", end="")
         asset.content = ''
     
     try:
         asset.reindex()
     
     except:
         return asset.path
     
     asset.save()
     return asset.path
Beispiel #11
0
 def gen():
     for record in Asset.objects(**aquery).only('title', 'description', 'path', 'acl').order_by('created'):
         yield 1.0, record
Beispiel #12
0
 def iterresults():
     for score, id_ in results:
         yield score, Asset.objects(id=id_, **aquery).only('title', 'description', 'path', 'acl').first()
Beispiel #13
0
 def __call__(self, value):
     from web.extras.contentment.components.asset.model import Asset
     
     if not value: return u''
     
     return u", ".join([Asset.objects(id=i.id).first().path for i in value])
Beispiel #14
0
    def post(self):
        print("Bootstrapping default site content...")
        
        from web.extras.contentment.components.asset.model import db, Asset, AdvancedACLRule, OwnerACLRule, UserACLRule, AllUsersACLRule, InheritACLRules
        from web.extras.contentment.components.page.model import Page
        from web.extras.contentment.components.folder.model import Folder
        from web.extras.contentment.components.identity.model import PasswordCredential, Identity
        from web.extras.contentment.components.authenticator.model import Authenticator
        from web.extras.contentment.components.search.model import Search
        from web.extras.contentment.components.settings.model import Settings
        from web.extras.contentment.themes.default.model import DefaultTheme
        
        settings = self.options
        
        scheme, parts = settings.db.split('://', 1)
        parts, db = parts.split('/', 1)
        auth, host = parts.split('@', 1) if '@' in parts else (None, parts)
        
        connection = dict()
        connection[b'host'], connection[b'port'] = host.split(':') if ':' in host else (host, '27017')
        connection[b'port'] = int(connection[b'port'])
        
        if auth:
            connection[b'username'], _, connection[b'password'] = auth.partition(':')
        
        mongoengine.connect(db, **connection)
        
        if Asset.objects().count() > 0:
            print("Existing content found, bootstrap cancelled.")
            # TODO: Interactively ask to overwrite data.
            return
        
        root = Asset(name="", path="/", title=settings['title'], default="default", immutable=True, properties={
                'org-contentment-formats-date': '%B %e, %G at %H:%M:%S',
                'org-contentment-theme': 'web.extras.contentment.themes.default',
                'org-contentment-option-attribution': True,
                'org-contentment-option-showdates': True,
                'org-contentment-lang': 'en',
                'org-contentment-cache': True
            })
        
        admin = Identity(name=settings['admin.name'], title=settings['admin.title'], email=settings['admin.email']) ; admin.save()
        
        root.acl.append(AdvancedACLRule(allow=False, permission="action:delete", attributes={'immutable': True}))
        root.acl.append(OwnerACLRule(allow=True, permission="*"))
        root.acl.append(UserACLRule(allow=True, permission="*", reference=admin))
        root.acl.append(AllUsersACLRule(allow=False, permission="view:acl"))
        root.acl.append(InheritACLRules())
        root.acl.append(AllUsersACLRule(allow=True, permission="view:*"))
        root.acl.append(AllUsersACLRule(allow=False, permission="*"))
        
        root.save()
        
        
        settings_ = Settings(name="settings", title="Site Settings", immutable=True, default="view:contents")
        settings_.acl.append(UserACLRule(allow=False, permission="*", inverse=True, reference=admin))
        settings_.save() ; settings_.attach(root)
        
        extensions = Folder(name="extensions", title="Site Extensions", immutable=True) ; extensions.save() ; extensions.attach(settings_)
        templates = Folder(name="templates", title="Site Templates", immutable=True) ; templates.save() ; templates.attach(settings_)
        custom = Folder(name="custom", title="Custom Page Templates", immutable=True) ; custom.save() ; custom.attach(templates)
        
        theme = DefaultTheme(name="theme", title="Default Theme", immutable=True); theme.save() ; theme.attach(root)
        
        users = Authenticator(name="users", title="Users", immutable=True)
        users.acl.append(AllUsersACLRule(allow=True, permission="action:authenticate"))
        users.acl.append(AllUsersACLRule(allow=True, permission="action:expire"))
        users.save() ; users.attach(root)
        
        password = PasswordCredential(identity=settings['admin.name'])
        password.password = settings['admin.password']
        admin.credentials.append(password) ; admin.save() ; admin.attach(users)
        
        header = Page(name="header", title="Global Site Header", content="""h1. "%s":/""" % (settings['title'], )) ; header.save() ; header.attach(templates)
        
        menu = Page(name="menu", title="Main Menu", engine="raw", content="""
<menu class="container">
    <li class="nav-default"><a href="/">Home<br><label>&nbsp;</label></a></li
    ><li class="nav-start"><a href="/start">Get Started<br><label>Getting Started with Contentment</label></a></li>
</menu>""") ; menu.save() ; menu.attach(templates)
        
        footer = Page(name="footer", title="Global Site Footer", engine="raw", content="""<p class="fr">© %s %s</p>

<menu>
    <li><a href="/about">About the Site</a></li
    ><li><a href="/about/privacy">Privacy Policy</a></li
    ><li><a href="/about/colophon">Colophon</a></li>
</menu>""" % (datetime.datetime.now().year, settings['admin.title'])) ; footer.save() ; footer.attach(templates)
        
        search = Search(name="search", title="Site Search") ; search.save() ; search.attach(root)
        
        default = Page(name="default", title="Welcome", owner=admin, content="""h1. Welcome to Contentment

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""
) ; default.save() ; default.attach(root)
        
        print("Finished bootstrapping the default site.")
Beispiel #15
0
 def getChildren(self, kind=None, tag=None, values=None, **kw):
     request = web.core.request
     response = web.core.response
     parent = self.controller.asset
     _latest = Asset.objects(parent=parent).only('created', 'modified')
     latest = None
     
     for i in _latest:
         if latest is None or i.created > latest:
             latest = i.created
         
         if i.modified and i.modified > latest:
             latest = i.modified
     
     response.last_modified = latest
     response.cache_control = 'public'
     response.etag = "%d" % ( int(time.mktime(latest.timetuple())) if latest else 0, )
     
     if latest and request.if_modified_since and request.if_modified_since >= response.last_modified:
         raise web.core.http.HTTPNotModified()
     
     if response.etag in request.if_none_match:
         raise web.core.http.HTTPNotModified()
     
     # The scanning (esp. security) is difficult to compute.
     # So we cache the result based on the above arguments and modification times.
     # TODO: Cached for one day; this should be configurable.
     # Initial timings (with debug logging enabled!) on the following complex query:
     #   kind=gallery & values=title,path & tag=!nav:hidden
     #   W/o cache: ~50/s, w/ cache: ~72/s.
     @web.core.cache.cache('page.content', expires=86400)
     def inner(parent_id, latest, kind, tag, values, kw):
         if kind is not None:
             kind = kind.lower().split(',')
         
         if values is None: values = 'name', 'title', 'description', 'path'
         else: values = values.split(',')
         
         for i in values:
             if i not in ('name', 'title', 'description', 'path', 'kind', 'rendered', 'tags', 'children', 'descendants'):
                 return 'json:', dict(status="error", message="Forbidden.")
         
         children = []
         
         def criteria(child):
             if kind is not None and child.__class__.__name__.lower() not in kind:
                 return False
             
             if tag is not None:
                 if tag[0] != '!' and tag not in child.tags:
                     return False
                 
                 elif tag[0] == '!' and tag in child.tags:
                     return False
             
             if not child.controller.allowed:
                 return False
             
             return True
         
         try:
             for child in parent.children:
                 if not criteria(child):
                     continue
                 
                 data = dict()
                 
                 for value in values:
                     if value == 'kind':
                         data[value] = child.__class__.__name__.lower()
                         continue
                     
                     if value == 'children':
                         # We won't return the actual child objects… just if we have children or not.
                         data[value] = bool(len(Asset.objects(parent=child).only('id')))
                         continue
                     
                     if value == 'descendants':
                         # Match child objects that also meet this search criteria.
                         data[value] = bool([i for i in Asset.objects(parent=child).only('acl', 'parent', 'parents', 'tags') if criteria(i)])
                         continue
                     
                     data[value] = getattr(child, value, '')
                 
                 children.append(data)
         
         except:
             log.exception("Error iterating children.")
             return 'json:', dict(status="error", message="Error determining asset contents.")
         
         return 'json:', dict(status="success", children=children)
     
     return inner(str(parent.id), latest, kind, tag, values, kw)
Beispiel #16
0
 def _save(self, action, asset, data):
     from web.extras.contentment.components.asset.model import Asset
     
     form = self._form(asset, web.core.request.referrer if action == 'create' else None, action.title())
     
     if not data:
         return action, dict(kind=asset.__class__.__name__, form=form, data=asset.prepare())
     
     data.pop('submit', None)
     
     try:
         result, remaining = form.native(data)
     
     except:
         if web.core.config.get('debug', False): raise
         
         log.exception("Error processing form.")
         web.core.session['flash'] = dict(cls="error", title="Server Error", message="Unable to create asset; see system log for details." % (asset.__class__.__name__, asset.title, asset.path))
         
         return action, dict(kind=asset.__class__.__name__, form=form, data=asset.prepare())
     
     # Root node must not be renamed.
     if asset.path == '/': del result['name']
     
     dirty = []
     
     siblings = [i.name for i in Asset.objects(parent=self.asset if action=="create" else self.asset.parent).only('name')]
     if asset.path != '/' and ( 'name' not in result or not result['name'] ):
         result['name'] = normalize(result['title'].lower(), siblings)
     
     elif asset.path != '/' and action != "create":
         result['name'] = normalize(result['name'].lower(), [i for i in siblings if i != asset.name])
     
     elif asset.path != '/' and action == "create":
         result['name'] = normalize(result['name'].lower(), siblings)
     
     del siblings
     
     if result.get('tags', None) is None:
         result['tags'] = []
     
     # Handle explicit setting of the modification time.
     if action == 'modify' and ( not result['modified'] or result['modified'] == asset.modified.replace(microsecond=0) ):
         result['modified'] = datetime.now(UTC)
     
     result = asset.process(result)
     
     for name, value in result.iteritems():
         if action == 'modify' and getattr(asset, name) == value: continue
         dirty.append(name)
         setattr(asset, name, value)
     
     try:
         asset.save(dirty=dirty)
     
     except:
         if web.core.config.get('debug', False):
             raise
         
         log.exception('Error saving record.')
         web.core.session['flash'] = dict(cls="error", title="Server Error", message="Unable to save asset; see system log for details." % (asset.__class__.__name__, asset.title, asset.path))
         return action, dict(kind=asset.__class__.__name__, form=form, data=asset.prepare())
     
     if action == 'create':
         asset.attach(self.asset)