Example #1
0
class PlominoDocument(CatalogAware, CMFBTreeFolder, Contained):
    """ These represent the contents in a Plomino database.

    A document contains *items* that may or may not correspond to fields on
    one or more forms.
    """

    security = ClassSecurityInfo()
    implements(interfaces.IPlominoDocument, IAttributeAnnotatable)

    portal_type = "PlominoDocument"
    meta_type = "PlominoDocument"

    security.declarePublic('__init__')

    def __init__(self, id):
        """ Initialization
        """
        CMFBTreeFolder.__init__(self, id)
        self.id = id
        self.items = PersistentDict()
        self.plomino_modification_time = DateTime().toZone('UTC')

    security.declarePublic('checkBeforeOpenDocument')

    def checkBeforeOpenDocument(self):
        """ Check read permission and open view.

        .. NOTE:: if ``READ_PERMISSION`` is set on the ``view`` action
            itself, it causes an error ('maximum recursion depth exceeded')
            if user hasn't permission.
        """
        if self.isReader():
            return self.OpenDocument()
        else:
            raise Unauthorized, "You cannot read this content"

    def doc_path(self):
        return self.getPhysicalPath()

    def doc_url(self):
        """ return valid and nice url:
        - hide plomino_documents
        - use physicalPathToURL if REQUEST available
        """
        path = self.doc_path()
        short_path = [p for p in path if p != "plomino_documents"]
        if hasattr(self, "REQUEST"):
            return self.REQUEST.physicalPathToURL(short_path)
        else:
            return "/".join(short_path)

    security.declarePublic('setItem')

    def setItem(self, name, value):
        """
        """
        items = self.items
        if type(value) == type(''):
            db = self.getParentDatabase()
            translation_service = getToolByName(db, 'translation_service')
            value = translation_service.asunicodetype(value)
        items[name] = value
        self.items = items
        self.plomino_modification_time = DateTime().toZone('UTC')

    security.declarePublic('getItem')

    def getItem(self, name, default=''):
        """
        """
        if (self.items.has_key(name)):
            return deepcopy(self.items[name])
        else:
            return default

    security.declarePublic('hasItem')

    def hasItem(self, name):
        """
        """
        return self.items.has_key(name)

    security.declarePublic('removeItem')

    def removeItem(self, name):
        """
        """
        if (self.items.has_key(name)):
            items = self.items
            del items[name]
            self.items = items

    security.declarePublic('getItems')

    def getItems(self):
        """
        """
        return self.items.keys()

    security.declarePublic('getItemClassname')

    def getItemClassname(self, name):
        """
        """
        return self.getItem(name).__class__.__name__

    security.declarePublic('getLastModified')

    def getLastModified(self, asString=False):
        """
        """
        if not hasattr(self, 'plomino_modification_time'):
            self.plomino_modification_time = self.bobobase_modification_time(
            ).toZone('UTC')
        if asString:
            return str(self.plomino_modification_time)
        else:
            return self.plomino_modification_time

    security.declarePublic('getRenderedItem')

    def getRenderedItem(self,
                        itemname,
                        form=None,
                        formid=None,
                        convertattachments=False):
        """ Return the item rendered according to the corresponding field.

        The used form can be, in order of precedence:
        - passed as the `form` parameter,
        - specified with the `formid` parameter and looked up,
        - looked up from the document.

        If no form or field is found, return the empty string.

        If `convertattachments` is True, then we assume that field
        attachments are text and append them to the rendered value.
        """
        db = self.getParentDatabase()
        result = ''
        if not form:
            if formid:
                form = db.getForm(formid)
            else:
                form = self.getForm()
        if form:
            field = form.getFormField(itemname)
            if field:
                result = field.getFieldRender(form, self, False)
                if (field.getFieldType() == 'ATTACHMENT'
                        and convertattachments):
                    result += ' ' + db.getIndex().convertFileToText(
                        self, itemname).decode('utf-8')
                    result = result.encode('utf-8')

        return result

    security.declarePublic('tojson')

    def tojson(self, REQUEST=None, item=None, formid=None, rendered=False):
        """return item value as JSON
        (return all items if item=None)
        """
        if not self.isReader():
            raise Unauthorized, "You cannot read this content"

        datatables_format = False
        if REQUEST:
            REQUEST.RESPONSE.setHeader('content-type',
                                       'application/json; charset=utf-8')
            item = REQUEST.get('item', item)
            formid = REQUEST.get('formid', formid)
            rendered_str = REQUEST.get('rendered', None)
            if rendered_str:
                rendered = True
            datatables_format_str = REQUEST.get('datatables', None)
            if datatables_format_str:
                datatables_format = True
        if not item:
            return json.dumps(self.items.data)

        if not formid:
            form = self.getForm()
        else:
            form = self.getParentDatabase().getForm(formid)
        if form:
            field = form.getFormField(item)
            if field:
                if field.getFieldType() == 'DATAGRID':
                    adapt = field.getSettings()
                    fieldvalue = adapt.getFieldValue(form, self, False, False,
                                                     REQUEST)
                    fieldvalue = adapt.rows(fieldvalue, rendered=rendered)
                    if datatables_format:
                        fieldvalue = {
                            'iTotalRecords': len(fieldvalue),
                            'aaData': fieldvalue
                        }
                else:
                    if rendered:
                        fieldvalue = self.getRenderedItem(item, form)
                    else:
                        adapt = field.getSettings()
                        fieldvalue = adapt.getFieldValue(
                            form, self, False, False, REQUEST)
            else:
                fieldvalue = self.getItem(item)
        else:
            fieldvalue = self.getItem(item)

        return json.dumps(fieldvalue)

    security.declarePublic('computeItem')

    def computeItem(self,
                    itemname,
                    form=None,
                    formid=None,
                    store=True,
                    report=True):
        """ return the item value according the formula of the field defined in
        the given form (use default doc form if None)
        and store the value in the doc (if store=True)
        """
        result = None
        db = self.getParentDatabase()
        if not form:
            if not formid:
                form = self.getForm()
            else:
                form = db.getForm(formid)
        if form:
            result = form.computeFieldValue(itemname, self, report=report)
            if store:
                self.setItem(itemname, result)
        return result

    security.declarePublic('getPlominoReaders')

    def getPlominoReaders(self):
        """
        """
        if self.hasItem('Plomino_Readers'):
            return asList(self.Plomino_Readers)
        else:
            return ['*']

    security.declarePublic('isReader')

    def isReader(self):
        """
        """
        return self.getParentDatabase().isCurrentUserReader(self)

    security.declarePublic('isAuthor')

    def isAuthor(self):
        """
        """
        return self.getParentDatabase().isCurrentUserAuthor(self)

    security.declareProtected(REMOVE_PERMISSION, 'delete')

    def delete(self, REQUEST=None):
        """delete the current doc
        """
        db = self.getParentDatabase()
        db.deleteDocument(self)
        if not REQUEST is None:
            return_url = REQUEST.get('returnurl')
            REQUEST.RESPONSE.redirect(return_url)

    security.declareProtected(EDIT_PERMISSION, 'validation_errors')

    def validation_errors(self, REQUEST):
        """check submitted values
        """
        db = self.getParentDatabase()
        form = db.getForm(REQUEST.get('Form'))

        errors = form.validateInputs(REQUEST, doc=self)
        if len(errors) > 0:
            return self.errors_json(errors=json.dumps({
                'success': False,
                'errors': errors
            }))
        else:
            return self.errors_json(errors=json.dumps({'success': True}))

    security.declareProtected(EDIT_PERMISSION, 'saveDocument')

    def saveDocument(self, REQUEST, creation=False):
        """save a document using the form submitted content
        """
        db = self.getParentDatabase()
        form = db.getForm(REQUEST.get('Form'))

        errors = form.validateInputs(REQUEST, doc=self)
        if len(errors) > 0:
            return form.notifyErrors(errors)

        self.setItem('Form', form.getFormName())

        # process editable fields (we read the submitted value in the request)
        form.readInputs(self, REQUEST, process_attachments=True)

        # refresh computed values, run onSave, reindex
        self.save(form, creation)

        redirect = REQUEST.get('plominoredirecturl')
        if not redirect:
            redirect = self.getItem("plominoredirecturl")
        if not redirect:
            redirect = self.absolute_url()
        REQUEST.RESPONSE.redirect(redirect)

    security.declareProtected(EDIT_PERMISSION, 'refresh')

    def refresh(self, form=None):
        """ re-compute fields and re-index document
        (onSave event is not called, and authors are not updated
        """
        self.save(form,
                  creation=False,
                  refresh_index=True,
                  asAuthor=False,
                  onSaveEvent=False)

    security.declareProtected(EDIT_PERMISSION, 'save')

    def save(self,
             form=None,
             creation=False,
             refresh_index=True,
             asAuthor=True,
             onSaveEvent=True):
        """refresh values according form, and reindex the document
        """
        # we process computed fields (refresh the value)
        if form is None:
            form = self.getForm()
        else:
            self.setItem('Form', form.getFormName())

        db = self.getParentDatabase()
        if form:
            for f in form.getFormFields(includesubforms=True,
                                        doc=self,
                                        applyhidewhen=False):
                mode = f.getFieldMode()
                fieldname = f.id
                if mode in ["COMPUTED", "COMPUTEDONSAVE"
                            ] or (mode == "CREATION" and creation):
                    result = form.computeFieldValue(fieldname, self)
                    self.setItem(fieldname, result)
                else:
                    # computed for display field are not stored
                    pass

            # compute the document title
            title_formula = form.getDocumentTitle()
            if title_formula:
                # Use the formula if we have one
                try:
                    title = self.runFormulaScript("form_" + form.id + "_title",
                                                  self, form.DocumentTitle)
                    if title != self.Title():
                        self.setTitle(title)
                except PlominoScriptException, e:
                    e.reportError('Title formula failed')
            elif creation:
                # If we have no formula and we're creating, use Form's title
                title = form.Title()
                if title != self.Title():
                    # We may be calling save with 'creation=True' on
                    # existing documents, in which case we may already have
                    # a title.
                    self.setTitle(title)

            # update the document id
            if creation and form.getDocumentId():
                new_id = self.generateNewId()
                if new_id:
                    transaction.savepoint(optimistic=True)
                    db.documents.manage_renameObject(self.id, new_id)

        # update the Plomino_Authors field with the current user name
        if asAuthor:
            authors = self.getItem('Plomino_Authors')
            name = db.getCurrentUser().getUserName()
            if authors == '':
                authors = []
                authors.append(name)
            elif name in authors:
                pass
            else:
                authors.append(name)
            self.setItem('Plomino_Authors', authors)

        # execute the onSaveDocument code of the form
        if form and onSaveEvent:
            try:
                result = self.runFormulaScript("form_" + form.id + "_onsave",
                                               self, form.onSaveDocument)
                if result and hasattr(self, 'REQUEST'):
                    self.REQUEST.set('plominoredirecturl', result)
            except PlominoScriptException, e:
                if hasattr(self, 'REQUEST'):
                    e.reportError(
                        'Document has been saved but onSave event failed.')
                    doc_path = self.REQUEST.physicalPathToURL(self.doc_path())
                    self.REQUEST.RESPONSE.redirect(doc_path)
Example #2
0
class AdmUtilEventCrossbar(Supernode):
    """Implementation of local EventCrossbar Utility"""

    implements(IAdmUtilEventCrossbar)

    lastEventCrossbar = "no signal since start"

    def __init__(self):
        Supernode.__init__(self)
        self.ikRevision = __version__
        self.inpEQueues = PersistentDict()
        self.outEQueues = PersistentDict()

    def receiveEventCrossbar(self, request, str_time, mode=None):
        """receive eventcrossbar signal
        """
        self.lastEventCrossbar = str_time
        dcore = IWriteZopeDublinCore(self)
        dcore.modified = datetime.now(berlinTZ)

    def getEventCrossbarTime(self):
        """get last eventcrossbar timestamp
        """
        try:
            retVal = self.lastEventCrossbar
        except NameError:
            self.__setattr__("lastEventCrossbar", "not set")
            retVal = self.lastEventCrossbar
        return retVal

    def fillDotFile(self, dotFile, mode=None):
        """generate the dot file
        """
        my_catalog = zapi.getUtility(ICatalog)
        objIdSet = set()
        objSet = set()
        eventSet = set()
        for (oid, oobj) in self.items():
            objIdSet.add(oid)
        for objId in self.inpEQueues:
            objIdSet.add(objId)
        for objId in self.outEQueues:
            objIdSet.add(objId)
        for objId in objIdSet:
            for result in my_catalog.searchResults(oid_index=objId):
                if IAdmUtilEvent.providedBy(result):
                    eventSet.add(result)
                elif IEventLogic.providedBy(result):
                    objSet.add(result)
                elif IEventLogic.providedBy(result):
                    objSet.add(result)
                elif IComponent.providedBy(result):
                    if result.isConnectedToEvent():
                        objSet.add(result)
                else:
                    pass
        print >> dotFile, '// GraphViz DOT-File'
        print >> dotFile, 'digraph "%s" {' % (zapi.getRoot(self).__name__)
        if mode and mode.lower() == "fview":
            print >> dotFile, '\tgraph [bgcolor="#E5FFF9", dpi="100.0"];'
        else:
            print >> dotFile, '\tgraph [bgcolor="#E5FFF9", size="6.2,5.2",' +\
            ' splines="true", ratio = "auto", dpi="100.0"];'
        print >> dotFile, '\tnode [fontname = "Helvetica",fontsize = 10];'
        print >> dotFile, '\tedge [style = "setlinewidth(2)", color = black];'
        print >> dotFile, '\trankdir = LR;'
        print >> dotFile, '\t// objects ----------------------------------'
        for obj in objSet:
            objGraphvizDot = IGenGraphvizDot(obj)
            objGraphvizDot.traverse4DotGenerator(dotFile,
                                                 level=1,
                                                 comments=True,
                                                 signalsOutput=True,
                                                 recursive=False)
        #print "-" * 80
        #uidutil = zapi.getUtility(IIntIds)
        #print >> dotFile, '\t// locations ----------------------------------'
        #for (oid, oobj) in uidutil.items():
        #if ILocation.providedBy(oobj.object):
        #print "Location: ", oobj.object.ikName
        #print >> dotFile, \
        #'\tsubgraph "cluster_location" { color=blue; label="location"};'
        ##elif IRoom.providedBy(oobj.object):
        ##print "Room: ", oobj.object.ikName
        ##elif ILocation.providedBy(oobj.object):
        ##print "Location: ", oobj.object.ikName
        #print "-" * 80

        print >> dotFile, '\t// events ----------------------------------'
        for event in eventSet:
            eventGraphvizDot = IGenGraphvizDot(event)
            eventGraphvizDot.traverse4DotGenerator(dotFile,
                                                   level=1,
                                                   comments=True)
        for obj in objSet:
            allInpNamesDict = obj.getAllInpEventNames()
            allOutNamesDict = obj.getAllOutEventNames()
            for inpName in allInpNamesDict.keys():
                for iObj in allInpNamesDict[inpName]:
                    print >> dotFile, '\t "%s"-> "%s":"%s"' % (
                        iObj, obj.objectID, inpName)
            for outName in allOutNamesDict.keys():
                for iObj in allOutNamesDict[outName]:
                    print >> dotFile, '\t "%s":"%s"-> "%s"' % (obj.objectID,
                                                               outName, iObj)
        print >> dotFile, '}'
        dotFile.flush()

    def getIMGFile(self, imgtype, mode=None):
        """get dot file and convert to png
        """
        dotFileName = '/tmp/dotFile_%s.dot' % os.getpid()
        outFileName = '/tmp/dotFile_%s.out' % os.getpid()
        dotFile = open(dotFileName, 'w')
        self.fillDotFile(dotFile, mode)
        dotFile.close()
        os.system("dot -T%s -o %s %s" % (imgtype, outFileName, dotFileName))
        pic = open(outFileName, "r")
        picMem = pic.read()
        pic.close()
        return picMem

    def getCmapxText(self, root_obj):
        """get dot file and convert to client side image map
        """
        its = root_obj.items()
        dotFileName = '/tmp/dotFile_%s.dot' % os.getpid()
        outFileName = '/tmp/dotFile_%s.out' % os.getpid()
        dotFile = open(dotFileName, 'w')
        self.fillDotFile(its, dotFile)
        dotFile.close()
        os.system("%s -Tcmapx -o %s %s" %
                  (self.graphviz_type, outFileName, dotFileName))
        mymap = open(outFileName, "r")
        mapMem = mymap.read()
        mymap.close()
        return mapMem

    def makeNewObjQueue(self, senderObj):
        """ will create a new input and output queue for this sender object """
        objId = senderObj.getObjectId()
        if not self.inpEQueues.has_key(objId):
            self.inpEQueues[objId] = Queue()
        if not self.outEQueues.has_key(objId):
            self.outEQueues[objId] = Queue()
        return True

    def destroyObjQueue(self, senderObj):
        """ will destroy the input and output queue for this sender object """
        objId = senderObj.getObjectId()
        if self.inpEQueues.has_key(objId):
            del self.inpEQueues[objId]
        if self.outEQueues.has_key(objId):
            del self.outEQueues[objId]
        return True

    def injectEventFromObj(self, senderObj, event):
        """ will inject an event from the sender object
        into the accordant queue """
        objId = senderObj.getObjectId()
        if self.inpEQueues.has_key(objId):
            self.inpEQueues[objId].put(event)
            return True
        return False

    def processOutEQueues(self):
        for objId in self.outEQueues:
            outQueue = self.outEQueues[objId]
            while len(outQueue) > 0:
                my_catalog = zapi.getUtility(ICatalog)
                foundObj = False
                for result in my_catalog.searchResults(oid_index=objId):
                    event = iter(outQueue).next()  # don't delete
                    if result.injectInpEQueue(event):
                        foundObj = True
                        outQueue.pull()  # now delete
                #if not foundObj:
                #print "delete trash"
                #outQueue.pull() # delete trash

    def processEvents(self):
        pass

    def processInpEQueues(self):
        my_catalog = zapi.getUtility(ICatalog)
        for senderOid in self.inpEQueues:
            inpQueue = self.inpEQueues[senderOid]
            while len(inpQueue) > 0:
                inpEvent = inpQueue.pull()
                processed = False
                for eventObj in self.values():
                    if IAdmUtilEvent.providedBy(eventObj):
                        if (senderOid in eventObj.inpObjects) and \
                           (eventObj.objectID == inpEvent.oidEventObject):
                            for receiverOid in eventObj.outObjects:
                                for receiverObj in my_catalog.searchResults(\
                                    oid_index=receiverOid):
                                    processed = True
                                    receiverObj.injectInpEQueue(inpEvent)
                if not processed:
                    for oid in self.outEQueues:
                        for receiverObj in my_catalog.searchResults(\
                            oid_index=oid):
                            if (inpEvent.oidEventObject in \
                                receiverObj.getAllInpEventObjs()):
                                processed = True
                                receiverObj.injectInpEQueue(inpEvent)
                if not processed:
                    inpEvent.stopit(self)

    def tickerEvent(self):
        ## debug if queue not empty
        #for qid in self.inpEQueues:
        #if len(self.inpEQueues[qid]) > 0:
        #logger.info("tickerEvent (n:%s, n(i):%s, n(o):%s)" % \
        #(qid,
        #len(self.inpEQueues[qid]),
        #len(self.outEQueues[qid])
        #))
        self.processOutEQueues()
        self.processEvents()
        self.processInpEQueues()

    def logIntoEvent(self, oidEventObject, logEntry):
        if self.has_key(oidEventObject):
            eventObject = self[oidEventObject]
            if eventObject.logAllEvents:
                newEntry = Entry(logEntry, eventObject, level=u"info")
                eventObject.history.append(newEntry)
                eventObject._p_changed = True

    def getEvent(self, oidEventObject):
        if self.has_key(oidEventObject):
            return self[oidEventObject]
        else:
            return None

    def debugEventHistory(self, eventObject=None):
        if eventObject is not None:
            my_catalog = zapi.getUtility(ICatalog)
            print "debugEventHistory:"
            print "-" * 40
            for entry in eventObject.transmissionHistory:
                for obj in my_catalog.searchResults(oid_index=entry):
                    print " -> ", obj.getDcTitle()
            print "-" * 40
Example #3
0
class OrderedContainer(Persistent, Contained):
    """ `OrderedContainer` maintains entries' order as added and moved.

    >>> oc = OrderedContainer()
    >>> int(IOrderedContainer.providedBy(oc))
    1
    >>> len(oc)
    0
    """

    implements(IOrderedContainer)

    def __init__(self):

        self._data = PersistentDict()
        self._order = PersistentList()

    def keys(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc.keys()
        ['foo']
        >>> oc['baz'] = 'quux'
        >>> oc.keys()
        ['foo', 'baz']
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return self._order[:]

    def __iter__(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc['baz'] = 'quux'
        >>> [i for i in oc]
        ['foo', 'baz']
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return iter(self.keys())

    def __getitem__(self, key):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc['foo'] = 'bar'
        >>> oc['foo']
        'bar'
        """

        return self._data[key]

    def get(self, key, default=None):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc['foo'] = 'bar'
        >>> oc.get('foo')
        'bar'
        >>> oc.get('funky', 'No chance, dude.')
        'No chance, dude.'
        """

        return self._data.get(key, default)

    def values(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc.values()
        ['bar']
        >>> oc['baz'] = 'quux'
        >>> oc.values()
        ['bar', 'quux']
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return [self._data[i] for i in self._order]

    def __len__(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> int(len(oc) == 0)
        1
        >>> oc['foo'] = 'bar'
        >>> int(len(oc) == 1)
        1
        """

        return len(self._data)

    def items(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc.items()
        [('foo', 'bar')]
        >>> oc['baz'] = 'quux'
        >>> oc.items()
        [('foo', 'bar'), ('baz', 'quux')]
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return [(i, self._data[i]) for i in self._order]

    def __contains__(self, key):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc['foo'] = 'bar'
        >>> int('foo' in oc)
        1
        >>> int('quux' in oc)
        0
        """

        return self._data.has_key(key)

    has_key = __contains__

    def __setitem__(self, key, object):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc._order
        ['foo']
        >>> oc['baz'] = 'quux'
        >>> oc._order
        ['foo', 'baz']
        >>> int(len(oc._order) == len(oc._data))
        1

        >>> oc['foo'] = 'baz'
        Traceback (most recent call last):
        ...
        KeyError: u'foo'
        >>> oc._order
        ['foo', 'baz']
        """

        existed = self._data.has_key(key)

        bad = False
        if isinstance(key, StringTypes):
            try:
                unicode(key)
            except UnicodeError:
                bad = True
        else:
            bad = True
        if bad:
            raise TypeError("'%s' is invalid, the key must be an "
                            "ascii or unicode string" % key)
        if len(key) == 0:
            raise ValueError("The key cannot be an empty string")

        # We have to first update the order, so that the item is available,
        # otherwise most API functions will lie about their available values
        # when an event subscriber tries to do something with the container.
        if not existed:
            self._order.append(key)

        # This function creates a lot of events that other code listens to.
        try:
            setitem(self, self._data.__setitem__, key, object)
        except Exception, e:
            if not existed:
                self._order.remove(key)
            raise e

        return key
Example #4
0
class PlominoDocument(CatalogAware, CMFBTreeFolder, Contained):
    """ These represent the contents in a Plomino database.

    A document contains *items*.
    An item may or may not correspond to fields on one or more forms.
    They may be manipulated by Formulas.
    """

    security = ClassSecurityInfo()
    implements(interfaces.IPlominoDocument, IAttributeAnnotatable)

    portal_type = "PlominoDocument"
    meta_type = "PlominoDocument"

    security.declarePublic('__init__')

    def __init__(self, id):
        """ Initialization
        """
        CMFBTreeFolder.__init__(self, id)
        self.id = id
        self.items = PersistentDict()
        self.plomino_modification_time = DateTime().toZone(TIMEZONE)

    security.declarePublic('checkBeforeOpenDocument')

    def checkBeforeOpenDocument(self):
        """ Check read permission and open view.

        .. NOTE:: if ``READ_PERMISSION`` is set on the ``view`` action
            itself, it causes an error ('maximum recursion depth exceeded')
            if user hasn't permission.
        """
        if self.isReader():
            return self.OpenDocument()
        else:
            raise Unauthorized, "You cannot read this content"

    def doc_path(self):
        return self.getPhysicalPath()

    def doc_url(self):
        """ Return valid and nice url:
        - hide plomino_documents
        - use physicalPathToURL if REQUEST available
        """
        path = self.doc_path()
        short_path = [p for p in path if p != "plomino_documents"]
        if hasattr(self, "REQUEST"):
            return self.REQUEST.physicalPathToURL(short_path)
        else:
            return "/".join(short_path)

    security.declarePublic('setItem')

    def setItem(self, name, value):
        """ Set item on document, converting str to unicode.
        """
        items = self.items
        if isinstance(value, str):
            db = self.getParentDatabase()
            translation_service = getToolByName(db, 'translation_service')
            value = translation_service.asunicodetype(value)
        items[name] = value
        self.items = items
        self.plomino_modification_time = DateTime().toZone(TIMEZONE)

    security.declarePublic('getItem')

    def getItem(self, name, default=''):
        """ Get item from document.
        """
        if self.items.has_key(name):
            return deepcopy(self.items[name])
        else:
            return default

    security.declarePublic('hasItem')

    def hasItem(self, name):
        """ Check if doc has item 'name'.
        """
        return self.items.has_key(name)

    security.declarePublic('removeItem')

    def removeItem(self, name):
        """ Delete item 'name', if it exists.
        """
        if self.items.has_key(name):
            items = self.items
            del items[name]
            self.items = items

    security.declarePublic('getItems')

    def getItems(self):
        """ Return all item names.
        """
        return self.items.keys()

    security.declarePublic('getItemClassname')

    def getItemClassname(self, name):
        """ Return class name of the item.
        """
        return self.getItem(name).__class__.__name__

    security.declarePublic('getLastModified')

    def getLastModified(self, asString=False):
        """ Return last modified date, setting it if absent.
        """
        if not hasattr(self, 'plomino_modification_time'):
            self.plomino_modification_time = self.bobobase_modification_time(
            ).toZone(TIMEZONE)
        if asString:
            return DateToString(self.plomino_modification_time,
                                db=self.getParentDatabase())
        else:
            return self.plomino_modification_time

    security.declarePublic('getRenderedItem')

    def getRenderedItem(self,
                        itemname,
                        form=None,
                        formid=None,
                        convertattachments=False):
        """ Return the item rendered according to the corresponding field.

        The form used can be, in order of precedence:
        - passed as the `form` parameter,
        - specified with the `formid` parameter and looked up,
        - looked up from the document.

        If no form or field is found, return the empty string.

        If `convertattachments` is True, then we assume that field
        attachments are text and append them to the rendered value.
        """
        db = self.getParentDatabase()
        result = ''
        if not form:
            if formid:
                form = db.getForm(formid)
            else:
                form = self.getForm()
        if form:
            field = form.getFormField(itemname)
            if field:
                result = field.getFieldRender(form, self, False)
                if (field.getFieldType() == 'ATTACHMENT'
                        and convertattachments):
                    result += ' ' + db.getIndex().convertFileToText(
                        self, itemname).decode('utf-8')
                    result = result.encode('utf-8')

        return result

    security.declarePublic('tojson')

    def tojson(self,
               REQUEST=None,
               item=None,
               formid=None,
               rendered=False,
               lastmodified=None):
        """ Return item value as JSON.

        Return all items if `item=None`.
        Values on the REQUEST overrides parameters.

        If the requested item corresponds to a field on the found form,
        the field value is returned. If not, it falls back to a plain item
        lookup on the document.

        `formid="None"` specifies plain item lookup.
        """
        # TODO: Don't always return the entire dataset: allow batching.

        if not self.isReader():
            raise Unauthorized, "You cannot read this content"

        datatables_format = False
        if REQUEST:
            REQUEST.RESPONSE.setHeader('content-type',
                                       'application/json; charset=utf-8')
            item = REQUEST.get('item', item)
            formid = REQUEST.get('formid', formid)
            lastmodified = REQUEST.get('lastmodified', lastmodified)
            rendered_str = REQUEST.get('rendered', None)
            if rendered_str:
                rendered = True
            datatables_format_str = REQUEST.get('datatables', None)
            if datatables_format_str:
                datatables_format = True
        if item:
            if formid == "None":
                form = None
            elif formid:
                form = self.getParentDatabase().getForm(formid)
            else:
                form = self.getForm()
            if form:
                field = form.getFormField(item)
                if field:
                    if field.getFieldType() == 'DATAGRID':
                        adapt = field.getSettings()
                        fieldvalue = adapt.getFieldValue(form,
                                                         doc=self,
                                                         request=REQUEST)
                        fieldvalue = adapt.rows(fieldvalue, rendered=rendered)
                    else:
                        if rendered:
                            fieldvalue = self.getRenderedItem(item, form)
                        else:
                            adapt = field.getSettings()
                            fieldvalue = adapt.getFieldValue(form,
                                                             doc=self,
                                                             request=REQUEST)
                else:
                    _logger.info("Failed to find %s on %s, "
                                 "fallback to getItem." % (item, form.id))
                    fieldvalue = self.getItem(item)
            else:
                fieldvalue = self.getItem(item)
            data = fieldvalue
        else:
            data = self.items.data

        if datatables_format:
            data = {
                'iTotalRecords': len(data),
                'iTotalDisplayRecords': len(data),
                'aaData': data
            }
        if lastmodified:
            data = {'lastmodified': self.getLastModified(), 'data': data}
        return json.dumps(data)

    security.declarePublic('computeItem')

    def computeItem(self,
                    itemname,
                    form=None,
                    formid=None,
                    store=True,
                    report=True):
        """ Return the value of named item according to the formula
        - of the field defined in the given form (default),
        - or the named `formid`,
        - or use the default doc form if no form found.
        Store the value in the doc (if `store=True`).
        (Pass `report` to `PlominoForm.computeFieldValue`.)
        """
        result = None
        if not form:
            if not formid:
                form = self.getForm()
            else:
                db = self.getParentDatabase()
                form = db.getForm(formid)
        if form:
            result = form.computeFieldValue(itemname, self, report=report)
            if store:
                # So this is a way to store the value of a DISPLAY field ..
                self.setItem(itemname, result)
        return result

    security.declarePublic('getPlominoReaders')

    def getPlominoReaders(self):
        """ Return list of readers; if none set, everyone can read.
        """
        if self.hasItem('Plomino_Readers'):
            return asList(self.getItem('Plomino_Readers'))
        else:
            return ['*']

    security.declarePublic('isReader')

    def isReader(self):
        """
        """
        return self.getParentDatabase().isCurrentUserReader(self)

    security.declarePublic('isAuthor')

    def isAuthor(self):
        """
        """
        return self.getParentDatabase().isCurrentUserAuthor(self)

    security.declareProtected(REMOVE_PERMISSION, 'delete')

    def delete(self, REQUEST=None):
        """ Delete the current doc; redirect to `returnurl` if available.
        """
        db = self.getParentDatabase()
        db.deleteDocument(self)
        if REQUEST:
            return_url = REQUEST.get('returnurl')
            if not return_url:
                return_url = db.absolute_url()
            REQUEST.RESPONSE.redirect(return_url)

    security.declareProtected(EDIT_PERMISSION, 'validation_errors')

    def validation_errors(self, REQUEST):
        """ Check submitted values.
        """
        db = self.getParentDatabase()
        form = db.getForm(REQUEST.get('Form'))
        errors = form.validateInputs(REQUEST, doc=self)
        if errors:
            return self.errors_json(errors=json.dumps({
                'success': False,
                'errors': errors
            }))
        else:
            return self.errors_json(errors=json.dumps({'success': True}))

    security.declareProtected(EDIT_PERMISSION, 'saveDocument')

    def saveDocument(self, REQUEST, creation=False):
        """ Save a document using the form submitted content
        """
        db = self.getParentDatabase()
        form = db.getForm(REQUEST.get('Form'))

        errors = form.validateInputs(REQUEST, doc=self)

        # execute the beforeSave code of the form
        error = None
        try:
            error = self.runFormulaScript(
                SCRIPT_ID_DELIMITER.join(['form', form.id, 'beforesave']),
                self, form.getBeforeSaveDocument)
        except PlominoScriptException, e:
            e.reportError('Form submitted, but beforeSave formula failed')

        if error:
            errors.append(error)

        # if errors, stop here, and notify errors to user
        if errors:
            return form.notifyErrors(errors)

        self.setItem('Form', form.getFormName())

        # process editable fields (we read the submitted value in the request)
        form.readInputs(self, REQUEST, process_attachments=True)

        # refresh computed values, run onSave, reindex
        self.save(form, creation)

        redirect = REQUEST.get('plominoredirecturl')
        if not redirect:
            redirect = self.getItem("plominoredirecturl")
        if type(redirect) is dict:
            # if dict, we assume it contains "callback" as an URL that will be
            # called asynchronously, "redirect" as the redirect url (optional,
            # default=doc url), and "method" (optional, default=GET)
            redirect = "./async_callback?" + urlencode(redirect)
        if not redirect:
            redirect = self.absolute_url()
        REQUEST.RESPONSE.redirect(redirect)
Example #5
0
class OrderedContainer(Persistent, Contained):
    """ `OrderedContainer` maintains entries' order as added and moved.

    >>> oc = OrderedContainer()
    >>> int(IOrderedContainer.providedBy(oc))
    1
    >>> len(oc)
    0
    """

    implements(IOrderedContainer)

    def __init__(self):

        self._data = PersistentDict()
        self._order = PersistentList()

    def keys(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc.keys()
        ['foo']
        >>> oc['baz'] = 'quux'
        >>> oc.keys()
        ['foo', 'baz']
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return self._order[:]

    def __iter__(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc['baz'] = 'quux'
        >>> [i for i in oc]
        ['foo', 'baz']
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return iter(self.keys())

    def __getitem__(self, key):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc['foo'] = 'bar'
        >>> oc['foo']
        'bar'
        """

        return self._data[key]

    def get(self, key, default=None):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc['foo'] = 'bar'
        >>> oc.get('foo')
        'bar'
        >>> oc.get('funky', 'No chance, dude.')
        'No chance, dude.'
        """

        return self._data.get(key, default)

    def values(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc.values()
        ['bar']
        >>> oc['baz'] = 'quux'
        >>> oc.values()
        ['bar', 'quux']
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return [self._data[i] for i in self._order]

    def __len__(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> int(len(oc) == 0)
        1
        >>> oc['foo'] = 'bar'
        >>> int(len(oc) == 1)
        1
        """

        return len(self._data)

    def items(self):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc.items()
        [('foo', 'bar')]
        >>> oc['baz'] = 'quux'
        >>> oc.items()
        [('foo', 'bar'), ('baz', 'quux')]
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        return [(i, self._data[i]) for i in self._order]

    def __contains__(self, key):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc['foo'] = 'bar'
        >>> int('foo' in oc)
        1
        >>> int('quux' in oc)
        0
        """

        return self._data.has_key(key)

    has_key = __contains__

    def __setitem__(self, key, object):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc._order
        ['foo']
        >>> oc['baz'] = 'quux'
        >>> oc._order
        ['foo', 'baz']
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        existed = self._data.has_key(key)

        bad = False
        if isinstance(key, StringTypes):
            try:
                unicode(key)
            except UnicodeError:
                bad = True
        else:
            bad = True
        if bad: 
            raise TypeError("'%s' is invalid, the key must be an "
                            "ascii or unicode string" % key)
        if len(key) == 0:
            raise ValueError("The key cannot be an empty string")

        setitem(self, self._data.__setitem__, key, object)

        if not existed:
            self._order.append(key)

        return key

    def __delitem__(self, key):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc.keys()
        []
        >>> oc['foo'] = 'bar'
        >>> oc['baz'] = 'quux'
        >>> oc['zork'] = 'grue'
        >>> oc.items()
        [('foo', 'bar'), ('baz', 'quux'), ('zork', 'grue')]
        >>> int(len(oc._order) == len(oc._data))
        1
        >>> del oc['baz']
        >>> oc.items()
        [('foo', 'bar'), ('zork', 'grue')]
        >>> int(len(oc._order) == len(oc._data))
        1
        """

        uncontained(self._data[key], self, key)
        del self._data[key]
        self._order.remove(key)

    def updateOrder(self, order):
        """ See `IOrderedContainer`.

        >>> oc = OrderedContainer()
        >>> oc['foo'] = 'bar'
        >>> oc['baz'] = 'quux'
        >>> oc['zork'] = 'grue'
        >>> oc.keys()
        ['foo', 'baz', 'zork']
        >>> oc.updateOrder(['baz', 'foo', 'zork'])
        >>> oc.keys()
        ['baz', 'foo', 'zork']
        >>> oc.updateOrder(['baz', 'zork', 'foo'])
        >>> oc.keys()
        ['baz', 'zork', 'foo']
        >>> oc.updateOrder(['baz', 'zork', 'foo'])
        >>> oc.keys()
        ['baz', 'zork', 'foo']
        >>> oc.updateOrder(('zork', 'foo', 'baz'))
        >>> oc.keys()
        ['zork', 'foo', 'baz']
        >>> oc.updateOrder(['baz', 'zork'])
        Traceback (most recent call last):
        ...
        ValueError: Incompatible key set.
        >>> oc.updateOrder(['foo', 'bar', 'baz', 'quux'])
        Traceback (most recent call last):
        ...
        ValueError: Incompatible key set.
        >>> oc.updateOrder(1)
        Traceback (most recent call last):
        ...
        TypeError: order must be a tuple or a list.
        >>> oc.updateOrder('bar')
        Traceback (most recent call last):
        ...
        TypeError: order must be a tuple or a list.
        >>> oc.updateOrder(['baz', 'zork', 'quux'])
        Traceback (most recent call last):
        ...
        ValueError: Incompatible key set.
        >>> del oc['baz']
        >>> del oc['zork']
        >>> del oc['foo']
        >>> len(oc)
        0
        """

        if not isinstance(order, ListType) and \
            not isinstance(order, TupleType):
            raise TypeError('order must be a tuple or a list.')

        if len(order) != len(self._order):
            raise ValueError("Incompatible key set.")

        was_dict = {}
        will_be_dict = {}
        new_order = PersistentList()

        for i in range(len(order)):
            was_dict[self._order[i]] = 1
            will_be_dict[order[i]] = 1
            new_order.append(order[i])

        if will_be_dict != was_dict:
            raise ValueError("Incompatible key set.")

        self._order = new_order
        notifyContainerModified(self)
class NyLocalizedBFile(NyContentData, NyAttributes, NyItem, NyCheckControl, NyValidation, NyContentType):
    """ """
    implements(INyBFile)

    meta_type = config['meta_type']
    meta_label = config['label']

    manage_options = (
        {'label': 'Properties', 'action': 'manage_edit_html'},
        {'label': 'Edit', 'action': 'manage_main'},
        {'label': 'View', 'action': 'index_html'},
    ) + NyItem.manage_options

    security = ClassSecurityInfo()

    def __init__(self, id, contributor):
        self.id = id
        NyContentData.__init__(self)
        NyValidation.__dict__['__init__'](self)
        NyCheckControl.__dict__['__init__'](self)
        NyItem.__dict__['__init__'](self)
        self.contributor = contributor
        self._versions = PersistentDict()

    security.declarePrivate('current_version')
    @property
    def current_version(self):
        language = self.get_selected_language()
        hasKey = self._versions.has_key(language)
        if hasKey == True:
            _versions = self._versions[language]
            for ver in reversed(_versions):
                if not ver.removed:
                    return ver
        else:
            return None

    security.declareProtected(view, 'current_version_download_url')
    def current_version_download_url(self):
        language = self.get_selected_language()
        versions = self._versions_for_tmpl(language)
        if versions:
            return versions[-1]['url']
        else:
            return None

    security.declarePrivate('remove_version')
    def remove_version(self, number, language, removed_by=None):

        if (not self._versions[language]) or (not self._versions[language][number]):
            raise ValueError # pick a random error

        ver = self._versions[language][number]

        if ver.removed:
            return

        ver.removed = True
        ver.removed_by = removed_by
        ver.removed_at = datetime.utcnow()
        ver.size = None

        f = ver.open_write()
        f.write('')
        f.close()

    def _save_file(self, the_file, language, contributor):
        """ """
        bf = make_blobfile(the_file,
                           removed=False,
                           timestamp=datetime.utcnow(),
                           contributor=contributor)
        _versions = self._versions.pop(language, None)

        if _versions == None:
            toAdd = [bf]
            newD = {language:toAdd}
            self._versions.update(newD)
        else:
            _versions.append(bf)
            newD = {language:_versions}
            self._versions.update(newD)

    def _versions_for_tmpl(self, language):
        """ """
        hasKey = self._versions.has_key(language)
        if hasKey == True:
            _versions = self._versions[language]
            versions = [tmpl_version(self, ver, str(n+1))
                        for n, ver in enumerate(_versions)
                        if not ver.removed]
            if versions:
                versions[-1]['is_current'] = True

            return versions
        else:
            return None

    security.declareProtected(view, 'index_html')
    def index_html(self, REQUEST=None, RESPONSE=None):
        """ """
        language = self.get_selected_language()
        versions = self._versions_for_tmpl(language)
        options = {'versions': versions}
        if versions:
            options['current_version'] = versions[-1]

        template_vars = {'here': self, 'options': options}
        to_return = self.getFormsTool().getContent(template_vars, 'localizedbfile_index')
        return to_return

    def isVersionable(self):
        """ Localized BFiles are not versionable"""
        return False

    security.declareProtected(PERMISSION_EDIT_OBJECTS, 'saveProperties')
    def saveProperties(self, REQUEST=None, **kwargs):
        """ """
        if not self.checkPermissionEditObject():
            raise EXCEPTION_NOTAUTHORIZED, EXCEPTION_NOTAUTHORIZED_MSG

        if REQUEST is not None:
            schema_raw_data = dict(REQUEST.form)
        else:
            schema_raw_data = kwargs
        _lang = schema_raw_data.pop('_lang', schema_raw_data.pop('lang', None))
        _releasedate = self.process_releasedate(schema_raw_data.pop('releasedate', ''), self.releasedate)
        _uploaded_file = schema_raw_data.pop('uploaded_file', None)
        versions_to_remove = schema_raw_data.pop('versions_to_remove', [])

        form_errors = self.process_submitted_form(schema_raw_data, _lang, _override_releasedate=_releasedate)

        if form_errors:
            if REQUEST is not None:
                self._prepare_error_response(REQUEST, form_errors, schema_raw_data)
                REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), _lang))
                return
            else:
                raise ValueError(form_errors.popitem()[1]) # pick a random error

        contributor = self.REQUEST.AUTHENTICATED_USER.getUserName()

        for ver_id in reversed(versions_to_remove):
            self.remove_version(int(ver_id) - 1, _lang, contributor)

        self._p_changed = 1
        self.recatalogNyObject(self)
        #log date
        auth_tool = self.getAuthenticationTool()
        auth_tool.changeLastPost(contributor)

        if file_has_content(_uploaded_file):
            self._save_file(_uploaded_file, _lang, contributor)

        notify(NyContentObjectEditEvent(self, contributor))

        if REQUEST:
            self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES,
                                     date=self.utGetTodayDate())
            REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' %
                                      (self.absolute_url(), _lang))

    security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_html')
    def edit_html(self, REQUEST=None, RESPONSE=None):
        """ """
        hasKey = REQUEST.form.has_key('lang')
        if hasKey == False:
            language = self.get_selected_language()
        else:
            language = REQUEST.form['lang']

        options = {'versions': self._versions_for_tmpl(language)}
        template_vars = {'here': self, 'options': options}
        to_return = self.getFormsTool().getContent(template_vars, 'localizedbfile_edit')

        return to_return

    security.declareProtected(view, 'download')
    download = CaptureTraverse(localizedbfile_download)
class NyLocalizedBFile(NyContentData, NyAttributes, NyItem, NyCheckControl,
                       NyValidation, NyContentType):
    """ """
    implements(INyBFile)

    meta_type = config['meta_type']
    meta_label = config['label']

    manage_options = (
        {
            'label': 'Properties',
            'action': 'manage_edit_html'
        },
        {
            'label': 'Edit',
            'action': 'manage_main'
        },
        {
            'label': 'View',
            'action': 'index_html'
        },
    ) + NyItem.manage_options

    security = ClassSecurityInfo()

    def __init__(self, id, contributor):
        self.id = id
        NyContentData.__init__(self)
        NyValidation.__dict__['__init__'](self)
        NyCheckControl.__dict__['__init__'](self)
        NyItem.__dict__['__init__'](self)
        self.contributor = contributor
        self._versions = PersistentDict()

    security.declarePrivate('current_version')

    @property
    def current_version(self):
        language = self.get_selected_language()
        hasKey = self._versions.has_key(language)
        if hasKey == True:
            _versions = self._versions[language]
            for ver in reversed(_versions):
                if not ver.removed:
                    return ver
        else:
            return None

    security.declareProtected(view, 'current_version_download_url')

    def current_version_download_url(self):
        language = self.get_selected_language()
        versions = self._versions_for_tmpl(language)
        if versions:
            return versions[-1]['url']
        else:
            return None

    security.declarePrivate('remove_version')

    def remove_version(self, number, language, removed_by=None):

        if (not self._versions[language]) or (
                not self._versions[language][number]):
            raise ValueError  # pick a random error

        ver = self._versions[language][number]

        if ver.removed:
            return

        ver.removed = True
        ver.removed_by = removed_by
        ver.removed_at = datetime.utcnow()
        ver.size = None

        f = ver.open_write()
        f.write('')
        f.close()

    def _save_file(self, the_file, language, contributor):
        """ """
        bf = make_blobfile(the_file,
                           removed=False,
                           timestamp=datetime.utcnow(),
                           contributor=contributor)
        _versions = self._versions.pop(language, None)

        if _versions == None:
            toAdd = [bf]
            newD = {language: toAdd}
            self._versions.update(newD)
        else:
            _versions.append(bf)
            newD = {language: _versions}
            self._versions.update(newD)

    def _versions_for_tmpl(self, language):
        """ """
        hasKey = self._versions.has_key(language)
        if hasKey == True:
            _versions = self._versions[language]
            versions = [
                tmpl_version(self, ver, str(n + 1))
                for n, ver in enumerate(_versions) if not ver.removed
            ]
            if versions:
                versions[-1]['is_current'] = True

            return versions
        else:
            return None

    security.declareProtected(view, 'index_html')

    def index_html(self, REQUEST=None, RESPONSE=None):
        """ """
        language = self.get_selected_language()
        versions = self._versions_for_tmpl(language)
        options = {'versions': versions}
        if versions:
            options['current_version'] = versions[-1]

        template_vars = {'here': self, 'options': options}
        to_return = self.getFormsTool().getContent(template_vars,
                                                   'localizedbfile_index')
        return to_return

    def isVersionable(self):
        """ Localized BFiles are not versionable"""
        return False

    security.declareProtected(PERMISSION_EDIT_OBJECTS, 'saveProperties')

    def saveProperties(self, REQUEST=None, **kwargs):
        """ """
        if not self.checkPermissionEditObject():
            raise EXCEPTION_NOTAUTHORIZED, EXCEPTION_NOTAUTHORIZED_MSG

        if REQUEST is not None:
            schema_raw_data = dict(REQUEST.form)
        else:
            schema_raw_data = kwargs
        _lang = schema_raw_data.pop('_lang', schema_raw_data.pop('lang', None))
        _releasedate = self.process_releasedate(
            schema_raw_data.pop('releasedate', ''), self.releasedate)
        _uploaded_file = schema_raw_data.pop('uploaded_file', None)
        versions_to_remove = schema_raw_data.pop('versions_to_remove', [])

        form_errors = self.process_submitted_form(
            schema_raw_data, _lang, _override_releasedate=_releasedate)

        if form_errors:
            if REQUEST is not None:
                self._prepare_error_response(REQUEST, form_errors,
                                             schema_raw_data)
                REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' %
                                          (self.absolute_url(), _lang))
                return
            else:
                raise ValueError(
                    form_errors.popitem()[1])  # pick a random error

        contributor = self.REQUEST.AUTHENTICATED_USER.getUserName()

        for ver_id in reversed(versions_to_remove):
            self.remove_version(int(ver_id) - 1, _lang, contributor)

        self._p_changed = 1
        self.recatalogNyObject(self)
        #log date
        auth_tool = self.getAuthenticationTool()
        auth_tool.changeLastPost(contributor)

        if file_has_content(_uploaded_file):
            self._save_file(_uploaded_file, _lang, contributor)

        notify(NyContentObjectEditEvent(self, contributor))

        if REQUEST:
            self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES,
                                     date=self.utGetTodayDate())
            REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' %
                                      (self.absolute_url(), _lang))

    security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_html')

    def edit_html(self, REQUEST=None, RESPONSE=None):
        """ """
        hasKey = REQUEST.form.has_key('lang')
        if hasKey == False:
            language = self.get_selected_language()
        else:
            language = REQUEST.form['lang']

        options = {'versions': self._versions_for_tmpl(language)}
        template_vars = {'here': self, 'options': options}
        to_return = self.getFormsTool().getContent(template_vars,
                                                   'localizedbfile_edit')

        return to_return

    security.declareProtected(view, 'download')
    download = CaptureTraverse(localizedbfile_download)