class SynchronizeRightActionsHandler(SynchronizeCategoryActionsHandler): ''' Implementation for a processor that synchronizes the actions of the groups in the configuration file with the database. ''' actionCategoryService = IActionRightService; wire.entity('actionCategoryService') rightService = IRightService; wire.entity('rightService') def __init__(self): super().__init__(Repository=RepositoryRight) def process(self, chain, solicit:Solicit, **keyargs): ''' @see: HandlerProcessor.process Synchronize the actions of the groups in the configuration file with the database. ''' assert isinstance(chain, Chain), 'Invalid chain %s' % chain assert isinstance(solicit, Solicit), 'Invalid solicit %s' % solicit assert isinstance(solicit.repository, RepositoryRight), 'Invalid repository %s' % solicit.repository rights = listBFS(solicit.repository, RepositoryRight.children, RepositoryRight.rightId) #group the actions by right name: rightId -> [actions] rightActions = self.groupActions(rights, RepositoryRight, 'rightId') self.synchronize(rightActions)
class VideoInfoServiceAlchemy(MetaInfoServiceBaseAlchemy, IVideoInfoService): ''' @see: IVideoInfoService ''' queryIndexer = IQueryIndexer wire.entity('queryIndexer') # The query indexer manages the query related information about plugins in order to be able to support the multi-plugin queries searchProvider = ISearchProvider wire.entity('searchProvider') # The search provider that will be used to manage all search related activities videoDataService = IVideoDataService wire.entity('videoDataService') #The correspondent meta data service for video def __init__(self): assert isinstance( self.queryIndexer, IQueryIndexer), 'Invalid IQueryIndexer %s' % self.queryIndexer assert isinstance(self.searchProvider, ISearchProvider ), 'Invalid search provider %s' % self.searchProvider assert isinstance( self.videoDataService, IVideoDataService ), 'Invalid video meta data service %s' % self.videoDataService MetaInfoServiceBaseAlchemy.__init__(self, VideoInfoMapped, QVideoInfo, VideoDataMapped, QVideoData, self.searchProvider, self.videoDataService, META_TYPE_KEY) self.queryIndexer.register(VideoInfoEntry, QVideoInfo, VideoDataEntry, QVideoData, META_TYPE_KEY)
class PersonIconServiceAlchemy(SessionSupport, IPersonIconService): ''' Implementation for @see: IPersonIconService ''' metaDataService = IMetaDataService; wire.entity('metaDataService') # provides the metadata service in order to retrieve metadata of the person icon userService = IUserService; wire.entity('userService') def __init__(self): ''' Construct the service ''' assert isinstance(self.metaDataService, IMetaDataService), 'Invalid metadata service %s' % self.metaDataService def getByPersonId(self, id, scheme='http', thumbSize=None): ''' @see: IPersonIconService.getById ''' personIcon = self.session().query(PersonIconMapped).get(id) if not personIcon: raise InputError(Ref(_('Invalid person icon'), ref=PersonIconMapped.Id)) assert isinstance(personIcon, PersonIconMapped) assert isinstance(self.metaDataService, IMetaDataService) metaData = self.metaDataService.getById(personIcon.MetaData, scheme, thumbSize) return metaData def setIcon(self, personId, metaDataId, update): ''' @see: IPersonIconService.setIcon ''' if update: user = User() user.Id = personId self.userService.update(user) entityDb = PersonIconMapped() entityDb.Id, entityDb.MetaData = personId, metaDataId try: self.session().merge(entityDb) self.session().flush((entityDb,)) except SQLAlchemyError as e: handle(e, entityDb) return entityDb.Id def detachIcon(self, personIconId): ''' @see: IPersonIconService.detachIcon ''' try: return self.session().query(PersonIconMapped).filter(PersonIconMapped.Id == personIconId).delete() > 0 except (OperationalError, IntegrityError): raise InputError(Ref(_('Can not detach person icon because in use'),))
class AudioDataServiceAlchemy(MetaDataServiceBaseAlchemy, IMetaDataReferencer, IAudioDataService): ''' Implementation for see @see: IAudioDataService ''' cdmArchiveAudio = ICDM; wire.entity('cdmArchiveAudio') thumbnailManager = IThumbnailManager; wire.entity('thumbnailManager') def __init__(self): assert isinstance(self.cdmArchiveAudio, ICDM), 'Invalid archive CDM %s' % self.cdmArchiveAudio assert isinstance(self.thumbnailManager, IThumbnailManager), 'Invalid thumbnail manager %s' % self.thumbnailManager MetaDataServiceBaseAlchemy.__init__(self, AudioDataMapped, QAudioData, self, self.cdmArchiveAudio, self.thumbnailManager)
class BlogThemeServiceAlchemy(EntityServiceAlchemy, IBlogThemeService): ''' Implementation for @see: IBlogThemeService ''' blogThemeCDM = ICDM wire.entity('blogThemeCDM') # The GUI resources CDM. def __init__(self): ''' Construct the blog theme service. ''' EntityServiceAlchemy.__init__(self, BlogThemeMapped, QBlogTheme) def upload(self, content): ''' @see IBlogThemeService.upload ''' # TODO: implement blog theme upload return False def delete(self, id): ''' @see IBlogThemeService.delete ''' # TODO: implement deletion of the blog theme content if local return super().delete(id)
class PluginService(ComponentServiceBase, IPluginService): ''' Provides the implementation for @see: IPluginService. ''' package = '__plugin__' # The top package where the plugins are configured Component = Plugin # The model class to use as a component. componentService = IComponentService; wire.entity('componentService') def __init__(self): ''' Constructs the plugins service. ''' assert isinstance(self.componentService, IComponentService), 'Invalid component service %s' % self.componentService super().__init__() def getById(self, id): ''' @see: IPluginService.getById ''' p = super().getById(id) assert isinstance(p, Plugin), 'Invalid plugin %s' % p try: p.Component = next(iter(self.componentService.getAll(q=QComponent(path=p.Path), limit=1))) except StopIteration: pass return p
class GatewayACLService(IGatewayACLService): ''' Implementation for @see: IGatewayACLService that provides the ACL gateways. ''' assemblyGroupGateways = Assembly; wire.entity('assemblyGroupGateways') # The assembly to be used for generating gateways def __init__(self): assert isinstance(self.assemblyGroupGateways, Assembly), \ 'Invalid assembly gateways %s' % self.assemblyGroupGateways self._processing = self.assemblyGroupGateways.create(solicit=Solicit) def getGateways(self, group): ''' @see: IGatewayACLService.getGateways ''' assert isinstance(group, str), 'Invalid group name %s' % group proc = self._processing assert isinstance(proc, Processing), 'Invalid processing %s' % proc solicit = proc.execute(FILL_CLASSES, solicit=proc.ctx.solicit(acl={group})).solicit assert isinstance(solicit, Solicit), 'Invalid solicit %s' % solicit return solicit.gateways or ()
class AnonymousGroupHandler(HandlerProcessor): ''' Provides the handler that injects the anonymous groups for gateways. ''' groupService = IGroupService wire.entity('groupService') def __init__(self): assert isinstance( self.groupService, IGroupService), 'Invalid group service %s' % self.groupService super().__init__() def process(self, chain, solicit: Solicit, **keyargs): ''' @see: HandlerProcessor.process Inject the groups identifiers. ''' assert isinstance(chain, Chain), 'Invalid chain %s' % chain assert isinstance(solicit, Solicit), 'Invalid solicit %s' % solicit assert solicit.acl is None, 'There is already an acl object %s' % solicit.acl solicit.acl = list( self.groupService.getAll(q=QGroup(isAnonymous=True))) if not solicit.acl: chain.cancel()
class RbacSupportAlchemy(SessionSupport, IRbacSupport): ''' Implementation for @see: IRbacSupport ''' rbacService = IRbacService wire.entity('rbacService') # Rbac service to use for complex role operations. def __init__(self): assert isinstance( self.rbacService, IRbacService), 'Invalid rbac service %s' % self.rbacService def iterateTypeAndRightsNames(self, rbacId): ''' @see: IRbacSupport.iterateTypeAndRightsNames ''' sql = self.session().query(RightMapped.Name, RightTypeMapped.Name).join(RightTypeMapped) sql = self.rbacService.rightsForRbacSQL(rbacId, sql=sql) sql = sql.order_by(RightTypeMapped.Name, RightMapped.Name) current = names = None for name, typeName in sql.all(): if current != typeName: if current is not None: yield current, names current = typeName names = [] names.append(name) if current is not None: yield current, names
class SynchronizeGroupActionsHandler(SynchronizeCategoryActionsHandler): ''' Implementation for a processor that synchronizes the actions of the groups in the configuration file with the database. ''' actionCategoryService = IActionGroupService; wire.entity('actionCategoryService') def __init__(self): super().__init__(Repository=RepositoryGroup) def process(self, chain, solicit:Solicit, **keyargs): ''' @see: HandlerProcessor.process Synchronize the actions of the groups in the configuration file with the database. ''' assert isinstance(chain, Chain), 'Invalid chain %s' % chain assert isinstance(solicit, Solicit), 'Invalid solicit %s' % solicit assert isinstance(solicit.repository, RepositoryGroup), 'Invalid repository %s' % solicit.repository groups = listBFS(solicit.repository, RepositoryGroup.children, RepositoryGroup.groupName) #first group the actions by group name: groupName -> [actions] groupActions = self.groupActions(groups, RepositoryGroup, 'groupName') self.synchronize(groupActions)
class RightServiceAlchemy(EntityGetServiceAlchemy, EntityCRUDServiceAlchemy, AclServiceAlchemy, CompensateServiceAlchemy, IRightService): ''' Implementation for @see: IRightService ''' signaturesRight = dict wire.entity('signaturesRight') def __init__(self): EntitySupportAlchemy.__init__(self, RightMapped, QRight) AclServiceAlchemy.__init__(self, RightMapped, RightAccess) CompensateServiceAlchemy.__init__(self, RightMapped, RightAccess, RightCompensate, signatures=self.signaturesRight) def getAll(self, typeName=None, q=None, **options): ''' @see: IRightService.getAll ''' sql = self.session().query(RightMapped.Id) if typeName: sql = sql.join(RightTypeMapped).filter( RightTypeMapped.Name == typeName) if q is not None: assert isinstance(q, QRight), 'Invalid query %s' % q sql = buildQuery(sql, q, RightMapped) return iterateCollection(sql, **options)
class GatewayService(IGatewayService): ''' Implementation for @see: IGatewayService that provides the default anonymous gateway data. ''' assemblyAnonymousGateways = Assembly wire.entity('assemblyAnonymousGateways') # The assembly to be used for generating gateways def __init__(self): assert isinstance(self.assemblyAnonymousGateways, Assembly), \ 'Invalid assembly gateways %s' % self.assemblyAnonymousGateways self._processing = self.assemblyAnonymousGateways.create(reply=Reply) def getAnonymous(self): ''' @see: IGatewayService.getAnonymous ''' proc = self._processing assert isinstance(proc, Processing), 'Invalid processing %s' % proc chain = Chain(proc) chain.process(reply=proc.ctx.reply()).doAll() reply = chain.arg.reply assert isinstance(reply, Reply), 'Invalid reply %s' % reply if Reply.gateways not in reply: return () return reply.gateways
class SynchronizeRightAccessHandler(SynchronizeCategoryAccessHandler): ''' Implementation for a processor that synchronizes the category (groups and rights) accesses in the configuration file with the database. ''' accessCategoryService = IRightService; wire.entity('accessCategoryService') def __init__(self): assert isinstance(self.accessCategoryService, IRightService), \ 'Invalid right service %s' % self.accessCategoryService super().__init__() def process(self, chain, solicit:Solicit, **keyargs): ''' @see: HandlerProcessor.process Synchronize the accesses in the configuration file with the accesses in the database. ''' assert isinstance(chain, Chain), 'Invalid chain %s' % chain assert isinstance(solicit, Solicit), 'Invalid solicit %s' % solicit #get the accesses for the rights and group them by rightId accesses = [acc for acc in filter(lambda access: access.rightId, solicit.syncAccesses)] grouped = self.groupByCategory(accesses, SyncAccess.rightId) self.syncAccessesWithDb(grouped) self.syncCompensatesWithDb(grouped)
class PersonIconServiceAlchemy(SessionSupport, IPersonIconService): ''' Implementation for @see: IPersonIconService ''' metaDataService = IMetaDataService; wire.entity('metaDataService') # provides the metadata service in order to retrieve metadata of the person icon def __init__(self): ''' Construct the service ''' assert isinstance(self.metaDataService, IMetaDataService), 'Invalid metadata service %s' % self.metaDataService def getByPersonId(self, id, scheme='http', thumbSize=None): ''' @see: IPersonIconService.getById ''' personIcon = self.session().query(PersonIconMapped).get(id) if not personIcon: raise InputError(Ref(_('Invalid person icon'), ref=PersonIconMapped.Id)) assert isinstance(personIcon, PersonIconMapped) assert isinstance(self.metaDataService, IMetaDataService) metaData = self.metaDataService.getById(personIcon.MetaData, scheme, thumbSize) return metaData def setIcon(self, personId, metaDataId): ''' @see: IPersonIconService.setIcon ''' entityDb = PersonIconMapped() entityDb.Id, entityDb.MetaData = personId, metaDataId try: self.session().merge(entityDb) self.session().flush((entityDb,)) except SQLAlchemyError as e: handle(e, entityDb) return entityDb.Id
class MemoryStatusPresenter: ''' Class providing the memory status presentation. ''' resourcesRoot = Node wire.entity('resourcesRoot') # The resources root node structure. def __init__(self): assert isinstance(self.resourcesRoot, Node), 'Invalid root node %s' % self.resourcesRoot node = NodePath(self.resourcesRoot, True, 'MemoryStatus') node.get = InvokerFunction(GET, self.present, typeFor(Non), [ Input('limit', typeFor(Integer), True, None), Input('include', typeFor(String), True, None), Input('exclude', typeFor(String), True, None), ], {}) def present(self, limit, include=None, exclude=None): ''' Provides the dictionary structure presenting the memory. Attention this will also call the garbage collection. @return: dictionary The dictionary containing the memory status. ''' if not limit: limit = 10 gc.collect() total, referencess = self.getRefcounts(limit, include, exclude) return {'References': {'Total': total, 'Class': referencess}} def getRefcounts(self, limit, prefixInclude, prefixExclude): counts = {} total = 0 for m in sys.modules.values(): for sym in dir(m): o = getattr(m, sym) typ = type(o) if isclass(typ): name = fullyQName(typ) if name not in counts: count = sys.getrefcount(o) counts[name] = count total += count # sort by refcount counts = [(name, count) for name, count in counts.items()] counts.sort(key=lambda pack: pack[1], reverse=True) d = OrderedDict() k = 0 for className, count in counts: add = True if prefixInclude: add = className.startswith(prefixInclude) if prefixExclude: add = not className.startswith(prefixExclude) if add: d[className] = str(count) if k >= limit: break k += 1 return str(total), d
class SynchronizeCategoryActionsHandler(HandlerProcessor): ''' Base implementation for a processor that synchronizes the actions of the groups and rights in the configuration file with the database. ''' actionCategoryService = IActionCategoryPrototype; wire.entity('actionCategoryService') def __init__(self, Repository): assert isinstance(self.actionCategoryService, IActionCategoryPrototype), \ 'Invalid action category service %s' % self.actionCategoryService super().__init__(Repository=Repository) def synchronize(self, entityActions): ''' Method to synchronize actions from configuration file with the database for groups and rights. @param entityActions: mapping entityId : list of actions ''' for entity, actions in entityActions.items(): actionsDb = set(self.actionCategoryService.getActions(entity)) if actions: actionsSet = set(action.path for action in actions) toDelete = actionsDb.difference(actionsSet) toAdd = actionsSet.difference(actionsDb) else: toDelete = actionsDb toAdd = None if toDelete: for path in toDelete: self.actionCategoryService.remAction(entity, path) if toAdd: for path in toAdd: self.actionCategoryService.addAction(entity, path) def groupActions(self, repositories, Repository, idName): ''' For a list of repositories, groups the actions by some Id attribute. @type Repository: Context @param idName: the name of the attribute representing the id of the entity (e.g groupName or rightId) @return: mapping Id : list of actions ''' groupActions = {} for repository in repositories: assert isinstance(repository, Repository), 'Invalid repository %s' % repository assert hasAttribute(Repository, idName), 'Invalid repository %s' % repository actions = groupActions.get(getattr(repository, idName)) if not actions: groupActions[getattr(repository, idName)] = repository.actions else: actions.extend(repository.actions) return groupActions
class AudioInfoServiceAlchemy(MetaInfoServiceBaseAlchemy, IAudioInfoService): ''' @see: IAudioInfoService ''' queryIndexer = QueryIndexer wire.entity('queryIndexer') def __init__(self): MetaInfoServiceBaseAlchemy.__init__(self, AudioInfoMapped, QAudioInfo, AudioDataMapped, QAudioData) self.queryIndexer.register(AudioInfoEntry, QAudioInfo, AudioDataEntry, QAudioData)
class ImageDataServiceAlchemy(MetaDataServiceBaseAlchemy, IMetaDataReferencer, IImageDataService): ''' @see: IImageDataService ''' cdmArchiveImage = ICDM wire.entity('cdmArchiveImage') thumbnailManager = IThumbnailManager wire.entity('thumbnailManager') def __init__(self): assert isinstance( self.cdmArchiveImage, ICDM), 'Invalid archive CDM %s' % self.cdmArchiveImage assert isinstance( self.thumbnailManager, IThumbnailManager ), 'Invalid thumbnail manager %s' % self.thumbnailManager MetaDataServiceBaseAlchemy.__init__(self, ImageDataMapped, QImageData, self, self.cdmArchiveImage, self.thumbnailManager)
class MetaInfoServiceAlchemy(MetaInfoServiceBaseAlchemy, IMetaInfoService): ''' Implementation for @see: IMetaInfoService ''' queryIndexer = IQueryIndexer wire.entity('queryIndexer') # The query indexer manages the query related information about plugins in order to be able to support the multi-plugin queries searchProvider = ISearchProvider wire.entity('searchProvider') # The search provider that will be used to manage all search related activities metaDataService = IMetaDataService wire.entity('metaDataService') #The correspondent meta data service for other media type def __init__(self): ''' Construct the meta info service. ''' assert isinstance( self.queryIndexer, IQueryIndexer), 'Invalid IQueryIndexer %s' % self.queryIndexer assert isinstance(self.searchProvider, ISearchProvider ), 'Invalid search provider %s' % self.searchProvider assert isinstance( self.metaDataService, IMetaDataService ), 'Invalid meta data service %s' % self.metaDataService MetaInfoServiceBaseAlchemy.__init__(self, MetaInfoMapped, QMetaInfo, MetaDataMapped, QMetaData, self.searchProvider, self.metaDataService, META_TYPE_KEY) self.queryIndexer.register(MetaInfoMapped, QMetaInfo, MetaDataMapped, QMetaData, META_TYPE_KEY)
class ArticleFileServiceAlchemy(EntityServiceAlchemy, IArticleFileService): ''' Implementation for @see: IArticleFileService ''' articleService = IArticleService wire.entity('articleService') # TODO: comment def __init__(self): ''' Construct the article file service. ''' EntityServiceAlchemy.__init__(self, ArticleFileMapped, QArticleFile)
class SynchronizeGroupsHandler(HandlerProcessor): ''' Implementation for a processor that synchronizes the groups in the configuration file with the database. ''' groupService = IGroupService wire.entity('groupService') anonymousGroups = set # The set with the anonymous groups names def __init__(self): assert isinstance(self.groupService, IGroupService) 'Invalid group service %s' % self.groupService assert isinstance( self.anonymousGroups, set), 'Invalid anonymous groups %s' % self.anonymousGroups super().__init__(Repository=RepositoryGroup) def process(self, chain, solicit: Solicit, **keyargs): ''' @see: HandlerProcessor.process Synchronize the groups of the groups in the configuration file with the database. ''' assert isinstance(chain, Chain), 'Invalid chain %s' % chain assert isinstance(solicit, Solicit), 'Invalid solicit %s' % solicit assert isinstance( solicit.repository, RepositoryGroup), 'Invalid repository %s' % solicit.repository #maps name to id groupsDb = {name: name for name in self.groupService.getAll()} #maps group_name to arguments required for group creation (None for groups) groups = { r.groupName: (self.createEntity, r) for r in listBFS(solicit.repository, RepositoryGroup.children, RepositoryGroup.groupName) } syncWithDatabase(self.groupService, groups, groupsDb) def createEntity(self, groupName): group = Group() group.Name = groupName group.IsAnonymous = groupName in self.anonymousGroups return group
class RbacPopulateRights(HandlerProcessorProceed): ''' Provides the handler that populates the rights based on RBAC structure. ''' rbacSupport = IRbacSupport wire.entity('rbacSupport') # Rbac support to use for complex role operations. def __init__(self): assert isinstance( self.rbacSupport, IRbacSupport), 'Invalid rbac support %s' % self.rbacSupport super().__init__() def process(self, solicitation: Solicitation, **keyargs): ''' @see: HandlerProcessorProceed.process Populate the rights. ''' assert isinstance( solicitation, Solicitation), 'Invalid solicitation %s' % solicitation assert isinstance(solicitation.rbacId, int), 'Invalid rbac Id %s' % solicitation.rbacId allTypes, rights, types = { aclType.name: aclType for aclType in solicitation.types }, [], [] for typeName, names in self.rbacSupport.iterateTypeAndRightsNames( solicitation.rbacId): aclType = allTypes.get(typeName) if not aclType: continue types.append(aclType) assert isinstance(aclType, TypeAcl) rights.extend(aclType.rightsFor(names)) solicitation.types = types if solicitation.rights is not None: solicitation.rights = chain(solicitation.rights, rights) else: solicitation.rights = rights
class ModelFiltersForPermissions(HandlerProcessorProceed): ''' Processor that provides the model filters on the resources permissions. ''' processorModelFilters = ProcessorModelFilters wire.entity('processorModelFilters') # The model filters processor. def __init__(self): ''' Construct the persistence invoker service. ''' assert isinstance(self.processorModelFilters, ProcessorModelFilters), \ 'Invalid model filters processor %s' % self.processorModelFilters super().__init__() def process(self, Permission: PermissionWithModelFilters, ModelFilter: ModelFilterResource, solicitation: Solicitation, **keyargs): ''' @see: HandlerProcessorProceed.process Process permission model fitler. ''' assert issubclass( Permission, PermissionResource), 'Invalid permission class %s' % Permission assert issubclass( ModelFilter, ModelFilterResource), 'Invalid model filter class %s' % ModelFilter assert isinstance( solicitation, Solicitation), 'Invalid solicitation %s' % solicitation assert isinstance( solicitation.permissions, Iterable), 'Invalid permissions %s' % solicitation.permissions solicitation.permissions = self.processorModelFilters.processPermissions( solicitation.permissions, ModelFilter)
class RegisterDatabaseGatewayHandler(HandlerProcessor): ''' Provides the handler that populates Gateway objects based on database GatewayData. ''' databaseGatewayProvider = DatabaseGatewayProviderAlchemy; wire.entity('databaseGatewayProvider') def __init__(self): assert isinstance(self.databaseGatewayProvider, DatabaseGatewayProviderAlchemy), \ 'Invalid database gateway provider %s' % self.databaseGatewayProvider super().__init__() def process(self, chain, solicit:Solicit, **keyargs): ''' @see: HandlerProcessor.process Adds the databse gateways. ''' assert isinstance(solicit, Solicit), 'Invalid solicit %s' % solicit gateways = self.databaseGatewayProvider.iterateGateways() if solicit.gateways is not None: solicit.gateways = itertools.chain(solicit.gateways, gateways) else: solicit.gateways = gateways
class UserRbacProvider(HandlerProcessor): ''' Provides the handler that extracts the rbac id for the user id. ''' userRbacSupport = IUserRbacSupport wire.entity('userRbacSupport') # The user rbac support use by the provider. def __init__(self): assert isinstance( self.userRbacSupport, IUserRbacSupport ), 'Invalid user rbac support %s' % self.userRbacSupport super().__init__() def process(self, chain, solicitation: Solicitation, **keyargs): ''' @see: HandlerProcessor.process Populate the rbac id. ''' assert isinstance(chain, Chain), 'Invalid chain %s' % chain assert isinstance( solicitation, Solicitation), 'Invalid solicitation %s' % solicitation assert isinstance( solicitation.userId, int), 'Invalid solicitation user id %s' % solicitation.userId solicitation.rbacId = self.userRbacSupport.rbacIdFor( solicitation.userId) if solicitation.rbacId is None: return # No rbac available so stopping the processing if Solicitation.provider in solicitation: solicitation.provider = UserProvider(str(solicitation.userId)) chain.proceed()
class JSONFileService(IJSONLocaleFileService): ''' Implementation for @see: IJSONLocaleFileService ''' default_charset = 'UTF-8' wire.config('default_charset', doc=''' The default character set to use whenever a JSON locale file is uploaded and the character set of the content is not specified''') poFileManager = IPOFileManager wire.entity('poFileManager') cdmLocale = ICDM wire.entity('cdmLocale') pluginService = IPluginService wire.entity('pluginService') componentService = IComponentService wire.entity('componentService') def __init__(self): assert isinstance( self.default_charset, str), 'Invalid default charset %s' % self.default_charset assert isinstance( self.poFileManager, IPOFileManager), 'Invalid PO file manager %s' % self.poFileManager assert isinstance(self.cdmLocale, ICDM), 'Invalid PO CDM %s' % self.cdmLocale assert isinstance( self.pluginService, IPluginService), 'Invalid plugin service %s' % self.pluginService assert isinstance( self.componentService, IComponentService ), 'Invalid component service %s' % self.componentService def getGlobalJSONFile(self, locale, scheme): ''' @see: IPOService.getGlobalPOFile ''' path = self._cdmPath(locale) try: try: cdmFileTimestamp = self.cdmLocale.getTimestamp(path) except PathNotFound: republish = True else: mngFileTimestamp = self.poFileManager.getGlobalPOTimestamp( locale) republish = False if mngFileTimestamp is None else cdmFileTimestamp < mngFileTimestamp if republish: jsonString = JSONEncoder(ensure_ascii=False).encode( self.poFileManager.getGlobalAsDict(locale)) self.cdmLocale.publishContent( path, BytesIO(bytes(jsonString, getdefaultencoding()))) except InvalidLocaleError: raise InputError( _('Invalid locale %(locale)s') % dict(locale=locale)) return self.cdmLocale.getURI(path, scheme) def getComponentJSONFile(self, component, locale, scheme): ''' @see: IPOService.getComponentPOFile ''' self.componentService.getById(component) path = self._cdmPath(locale, component=component) try: try: cdmFileTimestamp = self.cdmLocale.getTimestamp(path) except PathNotFound: republish = True else: mngFileTimestamp = max( self.poFileManager.getGlobalPOTimestamp(locale) or datetime.min, self.poFileManager.getComponentPOTimestamp( component, locale) or datetime.min) republish = False if mngFileTimestamp is None else cdmFileTimestamp < mngFileTimestamp if republish: jsonString = JSONEncoder(ensure_ascii=False).encode( self.poFileManager.getComponentAsDict(component, locale)) self.cdmLocale.publishContent( path, BytesIO(bytes(jsonString, getdefaultencoding()))) except InvalidLocaleError: raise InputError( _('Invalid locale %(locale)s') % dict(locale=locale)) return self.cdmLocale.getURI(path, scheme) def getPluginJSONFile(self, plugin, locale, scheme): ''' @see: IPOService.getPluginPOFile ''' pluginObj = self.pluginService.getById(plugin) assert isinstance(pluginObj, Plugin) if pluginObj.Component: return self.getComponentJSONFile(pluginObj.Component, locale, scheme) path = self._cdmPath(locale, plugin=plugin) try: try: cdmFileTimestamp = self.cdmLocale.getTimestamp(path) except PathNotFound: republish = True else: mngFileTimestamp = max( self.poFileManager.getGlobalPOTimestamp(locale) or datetime.min, self.poFileManager.getPluginPOTimestamp(plugin, locale) or datetime.min) republish = False if mngFileTimestamp is None else cdmFileTimestamp < mngFileTimestamp if republish: jsonString = JSONEncoder(ensure_ascii=False).encode( self.poFileManager.getPluginAsDict(plugin, locale)) self.cdmLocale.publishContent( path, BytesIO(bytes(jsonString, getdefaultencoding()))) except InvalidLocaleError: raise InputError( _('Invalid locale %(locale)s') % dict(locale=locale)) return self.cdmLocale.getURI(path, scheme) # ---------------------------------------------------------------- def _cdmPath(self, locale, component=None, plugin=None): ''' Returns the path to the CDM JSON file corresponding to the given locale and / or component / plugin. If no component of plugin was specified it returns the name of the global JSON file. @param locale: string The locale. @param component: string The component id. @param plugin: string The plugin id. @return: string The file path. ''' assert isinstance(locale, str), 'Invalid locale %s' % locale path = [] if component: path.append('component') path.append(component) elif plugin: path.append('plugin') path.append(plugin) else: path.append('global') path.append(locale) return '%s.json' % '-'.join(path)
class ContentPublisherService(IContentPublisherService): ''' Implementation for @see: IContentPublisherService ''' mongodb_server = 'localhost' wire.config('mongodb_server', doc='''The address of the mongoDb server''') mongodb_port = 27017 wire.config('mongodb_port', doc='''The port of the mongoDb server''') mongodb_database = 'mongodb' wire.config('mongodb_database', doc='''The name of the mongoDb database''') itemService = IItemService wire.entity('itemService') # item service used to convert article content to NewsML structure itemContentService = IItemContentService wire.entity('itemContentService') # item content service used to convert article content to NewsML structure def __init__(self): ''' Construct the content publisher service. ''' assert isinstance( self.mongodb_server, str), 'Invalid mongoDb server address %s' % self.mongodb_server assert isinstance( self.mongodb_port, int), 'Invalid mongoDb server port %s' % self.mongodb_port assert isinstance( self.mongodb_database, str), 'Invalid mongoDb database name %s' % self.mongodb_database mongoengine.connect(self.mongodb_database, host=self.mongodb_server, port=self.mongodb_port) def publish(self, guid): ''' Implementation for @see: IContentPublisherService.publish ''' # Test add document myItem = self.itemService.getById(guid) assert isinstance(myItem, PackageItem) item = Item() item.guid = myItem.GUId item.version = myItem.Version item.itemClass = myItem.ItemClass item.urgency = myItem.Urgency item.headline = myItem.HeadLine item.slugline = myItem.SlugLine item.byline = myItem.Byline item.creditline = myItem.CreditLine item.firstCreated = myItem.FirstCreated item.versionCreated = myItem.VersionCreated q = QItemContent() q.item = myItem.GUId contents = self.itemContentService.getAll(q=q) for c in contents: assert isinstance(c, ItemContent) content = Content() content.contenttype = c.ContentType content.content = c.Content content.residRef = c.ResidRef content.href = c.HRef content.size = c.Size content.rendition = c.Rendition item.contents.append(content) self.unpublish(item.guid) item.save(safe=True) return True def unpublish(self, guid): ''' Implementation for @see: IContentPublisherService.unpublish ''' # Test delete document Item.objects(guid=guid).delete(safe=True) return True
class Scanner: ''' The class that provides the scanner. ''' componentService = IComponentService wire.entity('componentService') pluginService = IPluginService wire.entity('pluginService') fileService = IFileService wire.entity('fileService') sourceService = ISourceService wire.entity('sourceService') messageService = IMessageService wire.entity('messageService') def __init__(self): ''' Construct the scanner. ''' assert isinstance(self.componentService, IComponentService), \ 'Invalid component service %s' % self.componentService assert isinstance( self.pluginService, IPluginService), 'Invalid plugin service %s' % self.pluginService assert isinstance( self.fileService, IFileService), 'Invalid file service %s' % self.fileService assert isinstance( self.sourceService, ISourceService), 'Invalid source service %s' % self.sourceService assert isinstance(self.messageService, IMessageService ), 'Invalid message service %s' % self.messageService @app.populate(app.CHANGED) def scanLocalization(self): ''' Scans the application for localization messages. ''' # log.info('Scanning the application distribution for localized messages') # self.scanComponents() # self.scanPlugins() # ---------------------------------------------------------------- def scanComponents(self): ''' Scan the current application components for the localized text messages. ''' for component in self.componentService.getComponents(): assert isinstance(component, Component) files = { file.Path: file for file in self.fileService.getAll(q=QFile( component=component.Id)) } if component.InEgg: lastModified = modificationTimeFor(component.Path) file = files.get(component.Path) if file and lastModified <= file.LastModified: log.info( 'No modifications for component zip file "%s" in %s', component.Path, component.Name) continue if not file: file = File() file.Component = component.Id file.Path = component.Path file.LastModified = lastModified files[component.Path] = file self.fileService.insert(file) else: file.LastModified = lastModified self.fileService.update(file) scanner = scanZip(component.Path) else: lastModified, scanner = None, scanFolder(component.Path) files.update({ source.Path: source for source in self.sourceService.getAll(q=QSource( component=component.Id)) }) self._persist(files, scanner, component.Path, lastModified, component.Id, None) def scanPlugins(self): ''' Scan the current application plugins for the localized text messages. ''' for plugin in self.pluginService.getPlugins(): assert isinstance(plugin, Plugin) files = { file.Path: file for file in self.fileService.getAll(q=QFile(plugin=plugin.Id)) } if plugin.InEgg: lastModified = modificationTimeFor(plugin.Path) file = files.get(plugin.Path) if file and lastModified <= file.LastModified: log.info('No modifications for plugin zip file "%s" in %s', plugin.Path, plugin.Name) continue if not file: file = File() file.Plugin = plugin.Id file.Path = plugin.Path file.LastModified = lastModified files[plugin.Path] = file self.fileService.insert(file) else: file.LastModified = lastModified self.fileService.update(file) scanner = scanZip(plugin.Path) else: lastModified, scanner = None, scanFolder(plugin.Path) files.update({ source.Path: source for source in self.sourceService.getAll(q=QSource( plugin=plugin.Id)) }) self._persist(files, scanner, plugin.Path, lastModified, None, plugin.Id) # ---------------------------------------------------------------- def _persist(self, files, scanner, path, lastModified, componentId, pluginId): ''' Persist the sources and messages. ''' assert isinstance(files, dict), 'Invalid files %s' % files processModified = lastModified is None for filePath, method, extractor in scanner: assert method in TYPES, 'Invalid method %s' % method file = files.get(filePath) if processModified: lastModified = modificationTimeFor(filePath) if file: assert isinstance(file, File) if lastModified <= file.LastModified: log.info('No modifications for file "%s"', filePath) continue file.LastModified = lastModified self.fileService.update(file) if isinstance(file, Source): source = file else: source = None messages = None try: for text, context, lineno, comments in extractor: if not source: if file: self.fileService.delete(file.Id) source = Source() source.Component = componentId source.Plugin = pluginId source.Path = filePath source.Type = method source.LastModified = lastModified files[filePath] = source self.sourceService.insert(source) if messages is None: messages = { msg.Singular: msg for msg in self.messageService.getMessages( source.Id) } if isinstance(text, str): singular, plurals = text, None elif len(text) == 1: singular, plurals = text[0], None else: singular, plurals = text[0], list(text[1:]) msg = messages.get(singular) if not msg: msg = Message() msg.Source = source.Id msg.Singular = singular msg.Plural = plurals msg.Context = context msg.LineNumber = lineno msg.Comments = '\n'.join(comments) self.messageService.insert(msg) messages[singular] = msg else: msg.Plural = plurals msg.Context = context msg.LineNumber = lineno msg.Comments = '\n'.join(comments) self.messageService.update(msg) except UnicodeDecodeError as e: log.error('%s: %s' % (filePath, str(e))) if processModified and filePath not in files: file = File() file.Component = componentId file.Plugin = pluginId file.Path = filePath file.LastModified = lastModified files[filePath] = file self.fileService.insert(file)
class ChainedSyncProcess: ''' Chained sync process. ''' blogSyncService = IBlogSyncService wire.entity('blogSyncService') # blog sync service used to retrieve blogs set on auto publishing sourceService = ISourceService wire.entity('sourceService') # source service used to retrieve source data blogPostService = IBlogPostService wire.entity('blogPostService') # blog post service used to insert blog posts postService = IPostService wire.entity('postService') # post service used to insert/update posts collaboratorService = ICollaboratorService wire.entity('collaboratorService') # blog post service used to retrive collaborator userService = IUserService wire.entity('userService') metaDataService = IMetaDataUploadService wire.entity('metaDataService') metaInfoService = IMetaInfoService wire.entity('metaInfoService') personIconService = IPersonIconService wire.entity('personIconService') syncThreads = {} # dictionary of threads that perform synchronization sync_interval = 53 wire.config('sync_interval', doc=''' The number of seconds to perform sync for blogs.''') timeout_inteval = 4 #; wire.config('timeout_interval', doc=''' #The number of seconds after the sync ownership can be taken.''') published_posts_path = 'Post/Published' wire.config('published_posts_path', doc=''' The partial path used to construct the URL for published posts retrieval''' ) user_type_key = 'chained blog' wire.config('user_type_key', doc=''' The user type that is used for the anonymous users of chained blog posts''' ) blog_provider_type = 'blog provider' wire.config('blog_provider_type', doc=''' Key of the source type for blog providers''') acceptType = 'text/json' # mime type accepted for response from remote blog encodingType = 'UTF-8' # character encoding type accepted for response from remove blog @app.deploy def startChainSyncThread(self): ''' Starts the chain sync thread. ''' schedule = scheduler(time.time, time.sleep) def syncChains(): self.syncChains() schedule.enter(self.sync_interval, 1, syncChains, ()) schedule.enter(self.sync_interval, 1, syncChains, ()) scheduleRunner = Thread(name='chained sync', target=schedule.run) scheduleRunner.daemon = True scheduleRunner.start() log.info('Started the chained blogs automatic synchronization.') def syncChains(self): ''' Read all chained blog sync entries and sync with the corresponding blogs. ''' log.info('Start chained blog synchronization') for blogSync in self.blogSyncService.getBySourceType( self.blog_provider_type): assert isinstance(blogSync, BlogSync) key = (blogSync.Blog, blogSync.Source) thread = self.syncThreads.get(key) if thread: assert isinstance(thread, Thread), 'Invalid thread %s' % thread if thread.is_alive(): log.info('Chained thread for blog %d is alive', blogSync.Blog) continue if not self.blogSyncService.checkTimeout( blogSync.Id, self.timeout_inteval * self.sync_interval): log.info('Chained thread for blog %d is already taken', blogSync.Blog) continue self.syncThreads[key] = Thread(name='blog %d sync' % blogSync.Blog, target=self._syncChain, args=(blogSync, )) self.syncThreads[key].daemon = True self.syncThreads[key].start() log.info('Chained thread started for blog id %d and source id %d', blogSync.Blog, blogSync.Source) log.info('End chained blog synchronization') def _syncChain(self, blogSync): ''' Synchronize the blog for the given sync entry. @param blogSync: BlogSync The blog sync entry declaring the blog and source from which the blog has to be updated. ''' assert isinstance(blogSync, BlogSync), 'Invalid blog sync %s' % blogSync source = self.sourceService.getById(blogSync.Source) log.info('_syncChain blogId=%d, sourceId=%d', blogSync.Blog, blogSync.Source) assert isinstance(source, Source) (scheme, netloc, path, params, query, fragment) = urlparse(source.URI) if not scheme: scheme = 'http' q = parse_qsl(query, keep_blank_values=True) q.append(('asc', 'cId')) q.append( ('cId.since', blogSync.CId if blogSync.CId is not None else 0)) url = urlunparse( (scheme, netloc, path + '/' + self.published_posts_path, params, urlencode(q), fragment)) req = Request(url, headers={ 'Accept': self.acceptType, 'Accept-Charset': self.encodingType, 'X-Filter': '*,Creator.*,Author.User.*,Author.Source.*', 'User-Agent': 'Magic Browser' }) try: resp = urlopen(req) except (HTTPError, socket.error) as e: log.error('Read error on %s: %s' % (source.URI, e)) blogSync.LastActivity = None self.blogSyncService.update(blogSync) return if str(resp.status) != '200': log.error('Read problem on %s, status: %s' % (source.URI, resp.status)) blogSync.LastActivity = None self.blogSyncService.update(blogSync) return try: msg = json.load(codecs.getreader(self.encodingType)(resp)) except ValueError as e: log.error('Invalid JSON data %s' % e) blogSync.LastActivity = None self.blogSyncService.update(blogSync) return usersForIcons = {} for post in msg['PostList']: try: if post['IsPublished'] != 'True': continue insert = False if 'Uuid' in post: uuid = post['Uuid'] localPost = self.postService.getByUuidAndSource( uuid, source.Id) else: #To support old instances that don't have Uuid attribute uuid = str(uuid4().hex) localPost = None if localPost == None: if 'DeletedOn' in post: continue localPost = Post() localPost.Uuid = uuid insert = True if 'DeletedOn' not in post: #TODO: workaround, read again the Author because sometimes we get access denied post['Author'] = self._readAuthor(post['Author']['href']) post['Creator'] = self._readCreator( post['Creator']['href']) #if exists local, update it, otherwise continue the original insert localPost.Type = post['Type']['Key'] localPost.Author, localPost.Creator, needUpdate, isAuthor = self._getCollaboratorForAuthor( post['Author'], post['Creator'], source) localPost.Feed = source.Id localPost.Meta = post['Meta'] if 'Meta' in post else None localPost.ContentPlain = post[ 'ContentPlain'] if 'ContentPlain' in post else None localPost.Content = post[ 'Content'] if 'Content' in post else None localPost.Order = post['Order'] if 'Order' in post else None localPost.CreatedOn = current_timestamp() if blogSync.Auto: localPost.PublishedOn = current_timestamp() localPost.WasPublished = True log.info("received post: %s", str(localPost)) if localPost.Creator and ( localPost.Creator not in usersForIcons) and needUpdate: try: if isAuthor: usersForIcons[ localPost.Creator] = post['Author']['User'] else: usersForIcons[ localPost.Creator] = post['Creator'] except KeyError: pass else: localPost.DeletedOn = datetime.strptime( post['DeletedOn'], '%m/%d/%y %I:%M %p') # prepare the blog sync model to update the change identifier blogSync.CId = int(post['CId']) if blogSync.CId is None or int( post['CId']) > blogSync.CId else blogSync.CId if insert: self.blogPostService.insert(blogSync.Blog, localPost) else: self.blogPostService.update(blogSync.Blog, localPost) # update blog sync entry blogSync.LastActivity = datetime.now().replace(microsecond=0) self.blogSyncService.update(blogSync) except KeyError as e: log.error('Post from source %s is missing attribute %s' % (source.URI, e)) except Exception as e: log.error('Error in source %s post: %s' % (source.URI, e)) self._updateIcons(usersForIcons) blogSync.LastActivity = None self.blogSyncService.update(blogSync) def _getCollaboratorForAuthor(self, author, creator, source): ''' Returns a collaborator identifier for the user/source defined in the post. If the post was not created by a user (it is twitter, facebook, etc. post) it returns a collaborator for the user that has added the post. @param author: dict The author data in JSON decoded format @param creator: dict The creator data in JSON decoded format @param source: Source The source from which the blog synchronization is done @return: integer The collaborator identifier. ''' assert isinstance(source, Source) user = User() isAuthor = False if 'User' in author: userJSON = author['User'] isAuthor = True else: userJSON = creator #To support old instances that don't have Uuid attribute if 'Uuid' in userJSON: user.Uuid = userJSON.get('Uuid', '') else: user.Uuid = str(uuid4().hex) if 'Cid' in userJSON: cid = int(userJSON.get('Cid', '')) else: cid = None user.Name = user.Uuid user.FirstName, user.LastName = userJSON.get('FirstName', ''), userJSON.get( 'LastName', '') user.Address, user.PhoneNumber = userJSON.get('Address', ''), userJSON.get( 'PhoneNumber', '') user.EMail, user.Password = userJSON.get('EMail', ''), '~' user.Type = self.user_type_key needUpdate = True try: userId = self.userService.insert(user) except InputError: localUser = self.userService.getByUuid(user.Uuid) userId = localUser.Id if localUser.Type == self.user_type_key and (cid is None or localUser.Cid < cid): user.Id = localUser.Id user.Type = localUser.Type user.Cid = cid self.userService.update(user) else: needUpdate = False collaborator = Collaborator() collaborator.User, collaborator.Source = userId, source.Id try: collaboratorId = self.collaboratorService.insert(collaborator) except InputError: collaborators = self.collaboratorService.getAll(userId, source.Id) collaboratorId = collaborators[0].Id if isAuthor: return [collaboratorId, userId, needUpdate, isAuthor] else: q = QSource(name=author['Source']['Name'], isModifiable=False) sources = self.sourceService.getAll(q=q) if not sources: raise Exception('Invalid source %s' % q.name) collaborators = self.collaboratorService.getAll( userId=None, sourceId=sources[0].Id) if collaborators: return [collaborators[0].Id, userId, needUpdate, isAuthor] else: collaborator = Collaborator() collaborator.Source = sources[0].Id return [ self.collaboratorService.insert(collaborator), userId, needUpdate, isAuthor ] def _updateIcons(self, usersData): ''' Setting the icon of the user ''' userIcons = {} for userId in usersData: userJSON = usersData[userId] userIcons[userId] = {'url': None, 'name': None} try: metaDataIconJSON = userJSON['MetaDataIcon'] metaDataIconURL = metaDataIconJSON.get('href', '') if not metaDataIconURL: continue (scheme, netloc, path, params, query, fragment) = urlparse(metaDataIconURL) if not scheme: metaDataIconURL = urlunparse( ('http', netloc, path, params, query, fragment)) req = Request(metaDataIconURL, headers={ 'Accept': self.acceptType, 'Accept-Charset': self.encodingType, 'User-Agent': 'Magic Browser' }) try: resp = urlopen(req) except (HTTPError, socket.error) as e: continue if str(resp.status) != '200': continue try: msg = json.load(codecs.getreader(self.encodingType)(resp)) except ValueError as e: log.error('Invalid JSON data %s' % e) continue userIcons[userId]['url'] = msg['Content'].get('href', None) if userIcons[userId]['url']: iconFileName = userIcons[userId]['url'].split('/')[-1] if iconFileName: iconFileName = '_' + iconFileName userIcons[userId]['name'] = 'icon_' + str( userId) + iconFileName except KeyError: continue for userId in userIcons: iconInfo = userIcons[userId] self._synchronizeIcon(userId, iconInfo) def _synchronizeIcon(self, userId, iconInfo): ''' Synchronizing local icon according to the remote one ''' if not userId: return shouldRemoveOld = False needToUploadNew = False try: metaDataLocal = self.personIconService.getByPersonId( userId, 'http') except InputError: metaDataLocal = None if metaDataLocal: localId = metaDataLocal.Id localName = metaDataLocal.Name else: localId = None localName = None if not localId: if iconInfo['url']: needToUploadNew = True else: if iconInfo['url']: #on changed avatar the name of the file is changed if (not iconInfo['name']) or (not localName) or ( localName != iconInfo['name']): shouldRemoveOld = True needToUploadNew = True else: shouldRemoveOld = True if shouldRemoveOld: try: self.personIconService.detachIcon(userId) #self.metaInfoService.delete(localId) except InputError: log.error('Can not remove old icon for chained user %s' % userId) if needToUploadNew: try: iconContent = ChainedIconContent(iconInfo['url'], iconInfo['name']) imageData = self.metaDataService.insert( userId, iconContent, 'http') if (not imageData) or (not imageData.Id): return self.personIconService.setIcon(userId, imageData.Id, False) except InputError: log.error('Can not upload icon for chained user %s' % userId) def _readAuthor(self, url): (scheme, netloc, path, params, query, fragment) = urlparse(url) if not scheme: url = urlunparse(('http', netloc, path, params, query, fragment)) request = Request(url, headers={ 'Accept': self.acceptType, 'Accept-Charset': self.encodingType, 'User-Agent': 'Magic Browser', 'X-Filter': '*,User.*,Source.*' }) try: response = urlopen(request) except (HTTPError, socket.error) as e: return None if str(response.status) != '200': return None try: return json.load(codecs.getreader(self.encodingType)(response)) except ValueError as e: log.error('Invalid JSON data %s' % e) return None def _readCreator(self, url): (scheme, netloc, path, params, query, fragment) = urlparse(url) if not scheme: url = urlunparse(('http', netloc, path, params, query, fragment)) request = Request(url, headers={ 'Accept': self.acceptType, 'Accept-Charset': self.encodingType, 'User-Agent': 'Magic Browser', 'X-Filter': '*' }) try: response = urlopen(request) except (HTTPError, socket.error) as e: return None if str(response.status) != '200': return None try: return json.load(codecs.getreader(self.encodingType)(response)) except ValueError as e: log.error('Invalid JSON data %s' % e) return None
class BlogCollaboratorServiceAlchemy(SessionSupport, IBlogCollaboratorService): ''' Implementation for @see: IBlogCollaboratorService ''' collaboratorSpecification = CollaboratorSpecification wire.entity('collaboratorSpecification') userActionService = IUserActionService wire.entity('userActionService') default_user_type_key = 'standard' wire.config('default_user_type_key', doc=''' Default user type for users without specified the user type key''') internal_source_name = 'internal' wire.config('internal_source_name', doc=''' Source for collaborators''') def __init__(self): ''' Construct the blog collaborator service. ''' assert isinstance(self.collaboratorSpecification, CollaboratorSpecification), \ 'Invalid collaborator specification %s' % self.collaboratorSpecification assert isinstance(self.userActionService, IUserActionService), \ 'Invalid user actions service %s' % self.userActionService super().__init__() self._collaboratorTypeIds = {} def getAllTypes(self): ''' @see: IBlogCollaboratorService.getAllTypes ''' return self.session().query(BlogCollaboratorTypeMapped).all() def getActions(self, userId, blogId, path=None, origPath=None): ''' @see: IBlogCollaboratorService.getActions ''' actions = list(self.userActionService.getAll(userId, path)) paths = {a.Path for a in actions} for name, f in self.collaboratorSpecification.type_filter: assert isinstance(f, Filter), 'Invalid filter' assert isinstance(f.filter, IAclFilter) if f.filter.isAllowed(userId, blogId): collActions = list( self.collaboratorSpecification.type_actions.get(name)) collPaths = {a.Path for a in collActions}.difference(paths) actions.extend([ action for action in collActions if action.Path in collPaths ]) break return actions def getById(self, blogId, collaboratorId): ''' @see: IBlogCollaboratorService.getById ''' sql = self.session().query(BlogCollaboratorMapped) sql = sql.filter(BlogCollaboratorMapped.Blog == blogId) sql = sql.filter(BlogCollaboratorMapped.Id == collaboratorId) try: return sql.one() except NoResultFound: raise InputError( Ref(_('No collaborator'), ref=BlogCollaboratorMapped.Id)) def getAll(self, blogId, offset=None, limit=None, detailed=True): ''' @see: IBlogCollaboratorService.getAll ''' sql = self.session().query(BlogCollaboratorMapped).filter( BlogCollaboratorMapped.Blog == blogId) sql = sql.join(UserMapped).join(SourceMapped).order_by( BlogCollaboratorMapped.Name) sql = sql.filter(UserMapped.Active == True) sql = sql.filter(UserMapped.Type == self.default_user_type_key) sqlLimit = buildLimits(sql, offset, limit) if detailed: return IterPart(sqlLimit.all(), sql.count(), offset, limit) return sqlLimit.all() def getPotential(self, blogId, excludeSources=True, offset=None, limit=None, detailed=True, qu=None, qs=None): ''' @see: IBlogCollaboratorService.getPotential ''' sqlBlog = self.session().query(BlogCollaboratorMapped.Id).filter( BlogCollaboratorMapped.Blog == blogId) sql = self.session().query(CollaboratorMapped) sql = sql.join(UserMapped, CollaboratorMapped.User == UserMapped.Id) sql = sql.join(SourceMapped, SourceMapped.Id == CollaboratorMapped.Source) sql = sql.filter(not_(CollaboratorMapped.Id.in_(sqlBlog))) sql = sql.filter(UserMapped.Active == True) sql = sql.filter(UserMapped.Type == self.default_user_type_key) sql = sql.filter(SourceMapped.Name == self.internal_source_name) sql = sql.order_by(CollaboratorMapped.Name) if excludeSources: sql = sql.filter(CollaboratorMapped.User != None) if qu: sql = buildQuery(sql, qu, UserMapped) if qs: sql = buildQuery(sql, qs, SourceMapped) sqlLimit = buildLimits(sql, offset, limit) if detailed: return IterPart(sqlLimit.distinct(), sql.distinct().count(), offset, limit) return sqlLimit.distinct() def addCollaboratorAsDefault(self, blogId, collaboratorId): ''' @see: IBlogCollaboratorService.addCollaboratorAsDefault ''' self.addCollaborator( blogId, collaboratorId, self.collaboratorSpecification.collaborator_types[0]) def addCollaborator(self, blogId, collaboratorId, typeName): ''' @see: IBlogCollaboratorService.addCollaborator ''' typeId = self.collaboratorTypeIds()[typeName] if typeId is None: raise InputError( Ref(_('Invalid collaborator type'), ref=BlogCollaborator.Type)) sql = self.session().query(BlogCollaboratorEntry) sql = sql.filter(BlogCollaboratorEntry.Blog == blogId) sql = sql.filter( BlogCollaboratorEntry.blogCollaboratorId == collaboratorId) if sql.update({BlogCollaboratorEntry.typeId: typeId}) > 0: return sql = self.session().query(BlogCollaboratorMapped.Id) sql = sql.join(BlogMapped) sql = sql.filter(BlogCollaboratorMapped.User == BlogMapped.Creator) sql = sql.filter(BlogMapped.Id == blogId) sql = sql.filter(BlogCollaboratorMapped.Id == collaboratorId) if sql.count() > 0: raise InputError( _('The blog creator cannot be assigned as a collaborator')) bgc = BlogCollaboratorEntry() bgc.Blog = blogId bgc.blogCollaboratorId = collaboratorId bgc.typeId = typeId self.session().add(bgc) self.session().flush((bgc, )) def removeCollaborator(self, blogId, collaboratorId): ''' @see: IBlogCollaboratorService.removeCollaborator ''' try: sql = self.session().query(BlogCollaboratorEntry) sql = sql.filter(BlogCollaboratorEntry.Blog == blogId) sql = sql.filter( BlogCollaboratorEntry.blogCollaboratorId == collaboratorId) return sql.delete() > 0 except OperationalError: raise InputError( Ref(_('Cannot remove'), model=BlogCollaboratorMapped)) # ---------------------------------------------------------------- def collaboratorTypeIds(self): ''' Provides the collaborator types ids dictionary. ''' if not self._collaboratorTypeIds: for name in self.collaboratorSpecification.collaborator_types: sql = self.session().query(BlogCollaboratorTypeMapped) sql = sql.filter(BlogCollaboratorTypeMapped.Name == name) try: bt = sql.one() except NoResultFound: bt = BlogCollaboratorTypeMapped() bt.Name = name self.session().add(bt) self.session().flush((bt, )) self._collaboratorTypeIds[name] = bt.id return self._collaboratorTypeIds