building a CMS for ambitious editors
history
born under the TYPO3 umbrella
goals
foundation
FLOW full stack PHP framework
yet an other framework
convention over configuration
security framework
privilegeTargets:
'TYPO3\Flow\Security\Authorization\Privilege\Method\MethodPrivilege':
'Acme.MyPackage:RestrictedController.customerAction': matcher: 'method(Acme\MyPackage\Controller\RestrictedController-> customerAction())'
'Acme.MyPackage:RestrictedController.adminAction': matcher: 'method(Acme\MyPackage\Controller\RestrictedController-> (admin|drop)Action())’
'Acme.MyPackage:editOwnPost': matcher: ‘method(Acme\MyPackage\Controller\PostController-> editAction(post.owner == current.userService.currentUser))'
roles: 'Acme.MyPackage:Customer': privileges: - privilegeTarget: 'Acme.MyPackage:RestrictedController.customerAction' permission: GRANT - privilegeTarget: 'Acme.MyPackage:RestrictedController.adminAction' permission: GRANT - privilegeTarget: 'Acme.MyPackage:RestrictedController.editOwnPost' permission: GRANT
'Acme.MyPackage:Customer': privileges: - privilegeTarget: 'Acme.MyPackage:RestrictedController.customerAction' permission: GRANT
'Acme.MyPackage:PrivilegedCustomer': parentRoles: ['Acme.MyPackage:Customer'] privileges: - privilegeTarget: 'Acme.MyPackage:RestrictedController.editOwnPost' permission: GRANT
security CLI helpers
$ ./flow security:showunprotectedactions
TYPO3\Neos\Controller\Backend\ContentController initializeUploadAssetAction initializeCreateImageVariantAction
TYPO3\Neos\Controller\LoginController initializeIndexAction
TYPO3\Neos\Controller\Service\WorkspacesController initializeUpdateAction
Ttree\CloudButler\Controller\JobController initializeRegisterAction
AOP
help with single responsibility principle
danger zone
We compile your code
Powerful Reflection API
@Flow\CompileStatic
/** * Returns a map of action method names and their parameters. * * @param \TYPO3\Flow\Object\ObjectManagerInterface $objectManager * @return array Array of method parameters by action name * @Flow\CompileStatic */ static public function getActionMethodParameters($objectManager) { $reflectionService = $objectManager->get( 'TYPO3\Flow\Reflection\ReflectionService'); $className = get_called_class(); $methodParameters = $reflectionService->getMethodParameters($className, get_class_methods($className)); foreach ($methodParameters as $parameterName => $parameterInfo) { ... } return $methodParameters; }
NEOS is just a package
NEOS Content Repository
where content lives
yaml configuration
'My.Package:SpecialHeadline': superTypes: 'TYPO3.Neos:Content': true ui: label: 'Special Headline' group: 'general' properties: headline: type: 'string' defaultValue: 'My Headline Default' ui: inlineEditable: true validation: 'TYPO3.Neos/Validation/StringLengthValidator': minimum: 1 maximum: 255
inspired by PHPCR
editing content
editor experience
workspace
content dimensions
contents lives in multiple contexts
WYSIFTW
developer
don’t hide features
create values
flexible content structure
modern rendering stack
fusion
prototype(Vendor:Staff) < prototype(TYPO3.Neos:Content) { templatePath = ‘resource://Vendor.Site/Private/Templates/ElementName.html'
headline = ${q(node).property('headline')} subheadline = ${q(node).property('subheadline')} text = ${q(node).property('text')} image = ${q(node).property('image')} }
prototype(Vendor:Staff) < prototype(TYPO3.Neos:Content) { templatePath = ‘resource://Vendor.Site/Private/Templates/ElementName.html'
headline = ${q(node).property('headline')} subheadline = ${q(node).property('subheadline')} text = ${q(node).property('text')} image = ${q(node).property(‘image’)}
staffIdentifier = ${q(node).property(‘staffIdentifier’)} [email protected] = ${ErpApi.getStaffDetails(value)} }
prototype(Vendor:YourTwoColumns) {
prototype(Vendor:Staff) { templatePath = ‘resource://Vendor.Site/Private/Templates/Compact.html' }
}
prototype(Vendor:StaffDirectory.Document) {
prototype(Vendor:Staff) { templatePath = ‘resource://Vendor.Site/Private/Templates/Square.html' staffPageLink = ${q(node).property(‘staffIdentifier’)} [email protected] = ${ErpApi.getStaffUri(value)} }
}
prototype(Vendor:StaffListing) < prototype(Flowpack.Listable:Listable) { @context.limit = 10 collection = ${q(site).find('[instanceof Vendor:Staff]’).get()} itemRenderer = ‘Vendor:StaffListingItem' }
prototype(Vendor:StaffListingItem) < prototype(Vendor:Staff) { templatePath = ‘resource://Vendor.Site/Private/Templates/ListingItem.html' }
cache on steroid
prototype(Vendor:StaffListingItem) { @cache { mode = 'cached'
maximumLifetime = '86400'
entryIdentifier { node = ${node} }
entryTags { 1 = ${'Node_' + documentNode.identifier} } }
}
much much more open to organize a workshop to build
something on top of NEOS
from the lab
CQRS for the content repository
touch points
neos.io
[email protected] - @ttreeagency - github.com/ttreeagency
tt ttreedigital beans