mirror of
https://github.com/a2nt/cms-niceties.git
synced 2024-10-22 11:05:46 +02:00
IMPR: GraphQL support
This commit is contained in:
parent
c0cf1c72c1
commit
a167ad4b5d
@ -28,4 +28,4 @@ SilverLeague\IDEAnnotator\DataObjectAnnotator:
|
|||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
SilverStripe\UserForms\Form\UserForm:
|
SilverStripe\UserForms\Form\UserForm:
|
||||||
show_labels: false
|
show_labels: true
|
||||||
|
37
_config/graphql.yml
Normal file
37
_config/graphql.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
Name: app-graphql
|
||||||
|
After:
|
||||||
|
- app-basics
|
||||||
|
Only:
|
||||||
|
classexists: 'SilverStripe\GraphQL\Schema\Schema'
|
||||||
|
---
|
||||||
|
|
||||||
|
SilverStripe\Core\Injector\Injector:
|
||||||
|
SilverStripe\GraphQL\Schema\Registry\PluginRegistry:
|
||||||
|
constructor:
|
||||||
|
- 'A2nt\CMSNiceties\GraphQL\URLLinkablePlugin'
|
||||||
|
|
||||||
|
SilverStripe\Control\Director:
|
||||||
|
rules:
|
||||||
|
graphql:
|
||||||
|
Controller: '%$SilverStripe\GraphQL\Controller.default'
|
||||||
|
Schema: default
|
||||||
|
|
||||||
|
SilverStripe\GraphQL\Schema\Schema:
|
||||||
|
schemas:
|
||||||
|
'*':
|
||||||
|
config:
|
||||||
|
max_query_nodes: 250 # default 500
|
||||||
|
max_query_depth: 20 # default 15
|
||||||
|
max_query_complexity: 200 # default unlimited
|
||||||
|
default:
|
||||||
|
src:
|
||||||
|
- app/_graphql
|
||||||
|
|
||||||
|
SilverStripe\GraphQLDevTools\Controller:
|
||||||
|
# show two schemas
|
||||||
|
schemas:
|
||||||
|
- default
|
||||||
|
- admin
|
||||||
|
# default schema that is selected
|
||||||
|
default_schema: default
|
30
_graphql/config.yml
Normal file
30
_graphql/config.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
modelConfig:
|
||||||
|
Page:
|
||||||
|
plugins:
|
||||||
|
inheritance: true
|
||||||
|
operations:
|
||||||
|
read:
|
||||||
|
plugins:
|
||||||
|
readVersion: false
|
||||||
|
paginateList: false
|
||||||
|
readOne:
|
||||||
|
plugins:
|
||||||
|
getByLink:
|
||||||
|
after: filter
|
||||||
|
|
||||||
|
DNADesign\Elemental\Models\ElementalArea:
|
||||||
|
plugins:
|
||||||
|
inheritance: true
|
||||||
|
operations:
|
||||||
|
read:
|
||||||
|
plugins:
|
||||||
|
readVersion: false
|
||||||
|
paginateList: false
|
||||||
|
DNADesign\Elemental\Models\BaseElement:
|
||||||
|
plugins:
|
||||||
|
inheritance: true
|
||||||
|
operations:
|
||||||
|
read:
|
||||||
|
plugins:
|
||||||
|
readVersion: false
|
||||||
|
paginateList: false
|
48
_graphql/models.yml
Normal file
48
_graphql/models.yml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
Page:
|
||||||
|
fields:
|
||||||
|
id: true
|
||||||
|
className: true
|
||||||
|
urlSegment: true
|
||||||
|
parentID: true
|
||||||
|
title: true
|
||||||
|
sort: true
|
||||||
|
CSSClass: true
|
||||||
|
MainContent:
|
||||||
|
type: String
|
||||||
|
showInMenus: Boolean
|
||||||
|
showInSearch: Boolean
|
||||||
|
link:
|
||||||
|
type: String
|
||||||
|
RequestLink:
|
||||||
|
type: String
|
||||||
|
children: "[Page]"
|
||||||
|
elementalArea:
|
||||||
|
type: ElementalArea
|
||||||
|
operations:
|
||||||
|
readOne:
|
||||||
|
plugins:
|
||||||
|
getByURL:
|
||||||
|
after: filter
|
||||||
|
getByLink:
|
||||||
|
after: filter
|
||||||
|
|
||||||
|
DNADesign\Elemental\Models\ElementalArea:
|
||||||
|
fields:
|
||||||
|
id: true
|
||||||
|
elements:
|
||||||
|
plugins:
|
||||||
|
paginateList: false
|
||||||
|
operations:
|
||||||
|
read: true
|
||||||
|
readOne: true
|
||||||
|
|
||||||
|
DNADesign\Elemental\Models\BaseElement:
|
||||||
|
fields:
|
||||||
|
id: true
|
||||||
|
title: true
|
||||||
|
showTitle: true
|
||||||
|
className: true
|
||||||
|
forTemplate: true
|
||||||
|
operations:
|
||||||
|
readOne: true
|
||||||
|
read: true
|
4
_graphql/schema.yml
Normal file
4
_graphql/schema.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
queries:
|
||||||
|
readMenu:
|
||||||
|
type: '[PageInterface]'
|
||||||
|
resolver: [ 'A2nt\CMSNiceties\GraphQL\MenuResolver', 'resolveMenu' ]
|
0
_graphql/types.yml
Normal file
0
_graphql/types.yml
Normal file
@ -7,6 +7,8 @@ use SilverStripe\Core\Extension;
|
|||||||
use SilverStripe\Forms\CompositeField;
|
use SilverStripe\Forms\CompositeField;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
|
use SilverStripe\Forms\TextareaField;
|
||||||
|
use SilverStripe\Forms\FormField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class \A2nt\CMSNiceties\Extensions\PlaceholderFormExtension
|
* Class \A2nt\CMSNiceties\Extensions\PlaceholderFormExtension
|
||||||
@ -24,7 +26,7 @@ class PlaceholderFormExtension extends Extension
|
|||||||
|
|
||||||
private function setPlaceholder($field)
|
private function setPlaceholder($field)
|
||||||
{
|
{
|
||||||
if (is_a($field, TextField::class)) {
|
if (is_a($field, TextField::class) || is_a($field, TextareaField::class)) {
|
||||||
if (!$field->getAttribute('placeholder')) {
|
if (!$field->getAttribute('placeholder')) {
|
||||||
$placeholder = $field->Title() .($field->hasClass('requiredField') ? '*' : '');
|
$placeholder = $field->Title() .($field->hasClass('requiredField') ? '*' : '');
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
use SilverStripe\Control\Director;
|
|
||||||
use SilverStripe\Control\HTTPRequest;
|
|
||||||
use SilverStripe\GraphQL\Auth\AuthenticatorInterface;
|
|
||||||
use SilverStripe\ORM\ValidationException;
|
|
||||||
use SilverStripe\Security\Member;
|
|
||||||
use A2nt\CMSNiceties\Templates\WebpackTemplateProvider;
|
|
||||||
use SilverStripe\Security\Permission;
|
|
||||||
use SilverStripe\Security\Security;
|
|
||||||
|
|
||||||
class APIKeyAuthenticator implements AuthenticatorInterface
|
|
||||||
{
|
|
||||||
public function authenticate(HTTPRequest $request)
|
|
||||||
{
|
|
||||||
$member = Security::getCurrentUser();
|
|
||||||
|
|
||||||
if (Director::isLive()
|
|
||||||
&& $request->getHeader('apikey') !== WebpackTemplateProvider::config()['GRAPHQL_API_KEY']
|
|
||||||
) {
|
|
||||||
if ($member && Permission::checkMember($member, 'CMS_ACCESS')) {
|
|
||||||
return $member;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ValidationException('Restricted resource', 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Member::get()->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isApplicable(HTTPRequest $request)
|
|
||||||
{
|
|
||||||
if ($request->param('Controller') === '%$SilverStripe\GraphQL\Controller.admin') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if($request->getHeader('apikey')){
|
|
||||||
return true;
|
|
||||||
}*/
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
|
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use SilverStripe\GraphQL\Middleware\QueryMiddleware;
|
|
||||||
use A2nt\CMSNiceties\Templates\WebpackTemplateProvider;
|
|
||||||
|
|
||||||
class APIKeyMiddleware implements QueryMiddleware
|
|
||||||
{
|
|
||||||
public function process(Schema $schema, $query, $context, $params, callable $next)
|
|
||||||
{
|
|
||||||
var_dump($context);
|
|
||||||
die('saaddsdsads');
|
|
||||||
if($request->getHeader('apikey') === WebpackTemplateProvider::config()['GRAPHQL_API_KEY']) {
|
|
||||||
return $next($schema, $query, $context, $params);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \Exception('Invalid API key token');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
if (!class_exists('SilverStripe\GraphQL\TypeCreator', true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\ResolveInfo;
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use SilverStripe\GraphQL\TypeCreator;
|
|
||||||
|
|
||||||
class ElementTypeCreator extends TypeCreator
|
|
||||||
{
|
|
||||||
public function attributes()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'element'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fields()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'_id' => ['type' => Type::nonNull(Type::id()),'resolve' => static function ($object) {
|
|
||||||
return $object->ID;
|
|
||||||
}],
|
|
||||||
'ID' => ['type' => Type::nonNull(Type::id())],
|
|
||||||
'Title' => ['type' => Type::string()],
|
|
||||||
'ParentID' => ['type' => Type::id()],
|
|
||||||
'Render' => [
|
|
||||||
'type' => Type::string(),
|
|
||||||
'resolve' => static function ($object, array $args, $context, ResolveInfo $info) {
|
|
||||||
return $object->getController()->forTemplate()->HTML();
|
|
||||||
}
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
if (!class_exists('SilverStripe\GraphQL\TypeCreator', true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use SilverStripe\GraphQL\TypeCreator;
|
|
||||||
use SilverStripe\GraphQL\Pagination\Connection;
|
|
||||||
|
|
||||||
class MemberTypeCreator extends TypeCreator
|
|
||||||
{
|
|
||||||
public function attributes()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'member'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fields()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'ID' => ['type' => Type::nonNull(Type::id())],
|
|
||||||
'Email' => ['type' => Type::string()],
|
|
||||||
'FirstName' => ['type' => Type::string()],
|
|
||||||
'Surname' => ['type' => Type::string()],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
37
src/GraphQL/MenuResolver.php
Normal file
37
src/GraphQL/MenuResolver.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace A2nt\CMSNiceties\GraphQL;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\ORM\Filterable;
|
||||||
|
|
||||||
|
class MenuResolver
|
||||||
|
{
|
||||||
|
public static function resolveLinksFilter(Filterable $list, array $args, array $context)
|
||||||
|
{
|
||||||
|
var_dump($context);
|
||||||
|
die('aaaa');
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function resolveMenu(): array
|
||||||
|
{
|
||||||
|
$pages = SiteTree::get()->filter('ParentID', '0');
|
||||||
|
$results = self::getFields($pages);
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getFields($pages): array
|
||||||
|
{
|
||||||
|
$results = [];
|
||||||
|
foreach ($pages as $p) {
|
||||||
|
$results[] = [
|
||||||
|
'id' => $p->ID,
|
||||||
|
'title' => $p->Title,
|
||||||
|
'children' => self::getFields($p->Children()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
}
|
@ -1,124 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
if (!class_exists('SilverStripe\GraphQL\TypeCreator', true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\ResolveInfo;
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use SilverStripe\CMS\Controllers\ModelAsController;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Control\Director;
|
|
||||||
use SilverStripe\Control\HTTPRequest;
|
|
||||||
use SilverStripe\Control\Session;
|
|
||||||
use SilverStripe\GraphQL\TypeCreator;
|
|
||||||
use SilverStripe\GraphQL\Pagination\Connection;
|
|
||||||
use SilverStripe\View\SSViewer;
|
|
||||||
|
|
||||||
class PageTypeCreator extends TypeCreator
|
|
||||||
{
|
|
||||||
public function attributes()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'page'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fields()
|
|
||||||
{
|
|
||||||
$elementsConnection = Connection::create('Elements')
|
|
||||||
->setConnectionType($this->manager->getType('element'))
|
|
||||||
->setDescription('A list of the page elements')
|
|
||||||
->setSortableFields(['ID', 'Title']);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'_id' => ['type' => Type::nonNull(Type::id()),'resolve' => static function ($object) {
|
|
||||||
return $object->ID;
|
|
||||||
}],
|
|
||||||
'ID' => ['type' => Type::nonNull(Type::id())],
|
|
||||||
'Title' => ['type' => Type::string()],
|
|
||||||
'Content' => ['type' => Type::string()],
|
|
||||||
'Link' => ['type' => Type::string(), 'resolve' => static function ($object) {
|
|
||||||
return $object->Link();
|
|
||||||
}],
|
|
||||||
'URLSegment' => ['type' => Type::string()],
|
|
||||||
'ParentID' => ['type' => Type::id()],
|
|
||||||
'ClassName' => ['type' => Type::string()],
|
|
||||||
'CSSClass' => ['type' => Type::string(), 'resolve' => static function ($object) {
|
|
||||||
return $object->CSSClass();
|
|
||||||
}],
|
|
||||||
'Summary' => ['type' => Type::string(), 'resolve' => static function ($object) {
|
|
||||||
return $object->Summary();
|
|
||||||
}],
|
|
||||||
'HTML' => ['type' => Type::string(), 'resolve' => static function ($object) {
|
|
||||||
// get action from request
|
|
||||||
$action = null;
|
|
||||||
|
|
||||||
/** @var \Page $object */
|
|
||||||
Director::set_current_page($object);
|
|
||||||
/** @var \PageController $controller */
|
|
||||||
$controller = ModelAsController::controller_for($object);
|
|
||||||
|
|
||||||
// find templates
|
|
||||||
$tpl = 'Page';
|
|
||||||
$tpls = SSViewer::get_templates_by_class(
|
|
||||||
$object->ClassName,
|
|
||||||
($action ? '_'.$action : ''),
|
|
||||||
\Page::class
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($tpls as $tpl) {
|
|
||||||
if (is_array($tpl)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$a_tpl = explode('\\', $tpl);
|
|
||||||
$last_name = array_pop($a_tpl);
|
|
||||||
$a_tpl[] = 'Layout';
|
|
||||||
$a_tpl[] = $last_name;
|
|
||||||
$a_tpl = implode('\\', $a_tpl);
|
|
||||||
|
|
||||||
if (SSViewer::hasTemplate($a_tpl)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
|
|
||||||
$tpl = ($tpl !== 'Page') ? $tpl : 'Layout/Page';
|
|
||||||
|
|
||||||
$action = $action ? $action : 'index';
|
|
||||||
/** @var HTTPRequest $request */
|
|
||||||
$request = new HTTPRequest('GET', $object->AbsoluteLink());
|
|
||||||
$request->setSession(new Session([]));
|
|
||||||
|
|
||||||
// a little dirty way to make forms working
|
|
||||||
Controller::curr()->config()->set('url_segment', $object->AbsoluteLink());
|
|
||||||
/*$controller->setRequest($request);*/
|
|
||||||
//$request->getSession()->init($request);
|
|
||||||
|
|
||||||
$controller->setRequest($request);
|
|
||||||
$controller->setAction($action);
|
|
||||||
//$controller->pushCurrent();
|
|
||||||
$controller->doInit();
|
|
||||||
|
|
||||||
$layout = $controller->renderWith($tpl);
|
|
||||||
return $controller
|
|
||||||
->customise(['Layout' => $layout])
|
|
||||||
->renderWith('GraphQLPage')->HTML();
|
|
||||||
}],
|
|
||||||
'Elements' => [
|
|
||||||
'type' => $elementsConnection->toType(),
|
|
||||||
'args' => $elementsConnection->args(),
|
|
||||||
'resolve' => static function ($object, array $args, $context, ResolveInfo $info) use ($elementsConnection) {
|
|
||||||
return $elementsConnection->resolveList(
|
|
||||||
$object->ElementalArea()->Elements(),
|
|
||||||
$args,
|
|
||||||
$context
|
|
||||||
);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
if (!class_exists('SilverStripe\GraphQL\Pagination\PaginatedQueryCreator', true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\ResolveInfo;
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use SilverStripe\Security\Member;
|
|
||||||
use SilverStripe\GraphQL\Pagination\Connection;
|
|
||||||
use SilverStripe\GraphQL\Pagination\PaginatedQueryCreator;
|
|
||||||
|
|
||||||
class PaginatedReadMembersQueryCreator extends PaginatedQueryCreator
|
|
||||||
{
|
|
||||||
public function createConnection()
|
|
||||||
{
|
|
||||||
|
|
||||||
return Connection::create('paginatedReadMembers')
|
|
||||||
->setConnectionType($this->manager->getType('member'))
|
|
||||||
->setArgs([
|
|
||||||
'Email' => [
|
|
||||||
'type' => Type::string()
|
|
||||||
]
|
|
||||||
])
|
|
||||||
->setSortableFields(['ID', 'FirstName', 'Email'])
|
|
||||||
->setConnectionResolver(static function ($object, array $args, $context, ResolveInfo $info) {
|
|
||||||
$member = Member::singleton();
|
|
||||||
if (!$member->canView($context['currentUser'])) {
|
|
||||||
throw new \InvalidArgumentException(sprintf(
|
|
||||||
'%s view access not permitted',
|
|
||||||
Member::class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
$list = Member::get();
|
|
||||||
|
|
||||||
// Optional filtering by properties
|
|
||||||
if (isset($args['Email'])) {
|
|
||||||
$list = $list->filter('Email', $args['Email']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $list;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
if (!class_exists('SilverStripe\GraphQL\Pagination\PaginatedQueryCreator', true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\ResolveInfo;
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\ORM\ArrayList;
|
|
||||||
use SilverStripe\ORM\SS_List;
|
|
||||||
use SilverStripe\Security\Member;
|
|
||||||
use SilverStripe\GraphQL\Pagination\Connection;
|
|
||||||
use SilverStripe\GraphQL\Pagination\PaginatedQueryCreator;
|
|
||||||
|
|
||||||
class PaginatedReadPagesQueryCreator extends PaginatedQueryCreator
|
|
||||||
{
|
|
||||||
public function createConnection()
|
|
||||||
{
|
|
||||||
|
|
||||||
return Connection::create('readPages')
|
|
||||||
->setConnectionType($this->manager->getType('page'))
|
|
||||||
->setArgs([
|
|
||||||
'Link' => [
|
|
||||||
'type' => Type::string()
|
|
||||||
]
|
|
||||||
])
|
|
||||||
->setSortableFields(['Sort'])
|
|
||||||
->setConnectionResolver(static function ($object, array $args, $context, ResolveInfo $info) {
|
|
||||||
|
|
||||||
if (isset($args['Link'])) {
|
|
||||||
$link = $args['Link'];
|
|
||||||
|
|
||||||
if (SiteTree::has_extension('\TractorCow\Fluent\Extension\FluentSiteTreeExtension')) {
|
|
||||||
$arr = array_filter(explode('/', $args['Link']));
|
|
||||||
|
|
||||||
$locale = \TractorCow\Fluent\Model\Locale::get()->filter('URLSegment', array_shift($arr))->first();
|
|
||||||
\TractorCow\Fluent\State\FluentState::singleton()->setLocale($locale->Locale);
|
|
||||||
|
|
||||||
$link = implode('/', $arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$list = ArrayList::create();
|
|
||||||
$page = SiteTree::get_by_link($link);
|
|
||||||
$list->add($page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*$list = \Page::get();
|
|
||||||
|
|
||||||
// Optional filtering by properties
|
|
||||||
if (isset($args['ID'])) {
|
|
||||||
$list = $list->filter('ID', $args['ID']);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return $list;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace A2nt\CMSNiceties\GraphQL;
|
|
||||||
|
|
||||||
if (!class_exists('SilverStripe\GraphQL\QueryCreator', true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\ResolveInfo;
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use SilverStripe\Security\Member;
|
|
||||||
use SilverStripe\GraphQL\OperationResolver;
|
|
||||||
use SilverStripe\GraphQL\QueryCreator;
|
|
||||||
|
|
||||||
class ReadMembersQueryCreator extends QueryCreator implements OperationResolver
|
|
||||||
{
|
|
||||||
public function attributes()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'readMembers'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function args()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'Email' => ['type' => Type::string()]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function type()
|
|
||||||
{
|
|
||||||
return Type::listOf($this->manager->getType('member'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function resolve($object, array $args, $context, ResolveInfo $info)
|
|
||||||
{
|
|
||||||
$member = Member::singleton();
|
|
||||||
|
|
||||||
if (!$member->canView($context['currentUser'])) {
|
|
||||||
throw new \InvalidArgumentException(sprintf(
|
|
||||||
'%s view access not permitted',
|
|
||||||
Member::class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
$list = Member::get();
|
|
||||||
|
|
||||||
// Optional filtering by properties
|
|
||||||
if (isset($args['Email'])) {
|
|
||||||
$list = $list->filter('Email', $args['Email']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $list;
|
|
||||||
}
|
|
||||||
}
|
|
272
src/GraphQL/URLLinkablePlugin.php
Normal file
272
src/GraphQL/URLLinkablePlugin.php
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace A2nt\CMSNiceties\GraphQL;
|
||||||
|
|
||||||
|
use GraphQL\Type\Definition\ResolveInfo;
|
||||||
|
use SilverStripe\CMS\Controllers\ModelAsController;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\Core\Config\Configurable;
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
|
use SilverStripe\Dev\Debug;
|
||||||
|
use SilverStripe\GraphQL\Controller;
|
||||||
|
use SilverStripe\GraphQL\Schema\Field\ModelQuery;
|
||||||
|
use SilverStripe\GraphQL\Schema\Interfaces\ModelQueryPlugin;
|
||||||
|
use SilverStripe\GraphQL\Schema\Schema;
|
||||||
|
use SilverStripe\ORM\ArrayList;
|
||||||
|
use SilverStripe\View\ArrayData;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
|
|
||||||
|
if (!interface_exists(ModelQueryPlugin::class)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
class URLLinkablePlugin implements ModelQueryPlugin
|
||||||
|
{
|
||||||
|
use Configurable;
|
||||||
|
use Injectable;
|
||||||
|
|
||||||
|
public const IDENTIFIER = 'getByURL';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @config
|
||||||
|
*/
|
||||||
|
private static $single_field_name = 'RequestLink';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @config
|
||||||
|
*/
|
||||||
|
private static $list_field_name = 'RequestLinks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $resolver = [__CLASS__, 'applyURLFilter'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier(): string
|
||||||
|
{
|
||||||
|
return self::IDENTIFIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ModelQuery $query
|
||||||
|
* @param Schema $schema
|
||||||
|
* @param array $config
|
||||||
|
*/
|
||||||
|
public function apply(ModelQuery $query, Schema $schema, array $config = []): void
|
||||||
|
{
|
||||||
|
$class = $query->getModel()->getSourceClass();
|
||||||
|
// Only site trees have the get_by_link capability
|
||||||
|
/*if ($class !== SiteTree::class && !is_subclass_of($class, SiteTree::class)) {
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
$singleFieldName = $this->config()->get('single_field_name');
|
||||||
|
$listFieldName = $this->config()->get('list_field_name');
|
||||||
|
$fieldName = $query->isList() ? $listFieldName : $singleFieldName;
|
||||||
|
$type = $query->isList() ? '[String]' : 'String';
|
||||||
|
$query->addArg($fieldName, $type);
|
||||||
|
$query->addResolverAfterware(
|
||||||
|
$config['resolver'] ?? static::config()->get('resolver')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $context
|
||||||
|
* @return callable
|
||||||
|
*/
|
||||||
|
public static function applyURLFilter($obj, array $args, array $context, ResolveInfo $info)
|
||||||
|
{
|
||||||
|
$url = self::getURL($args);
|
||||||
|
if (!$url) {
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
$controller = self::getURLController($url);
|
||||||
|
|
||||||
|
$obj = $controller->data();
|
||||||
|
$obj->GraphQLContent = self::RenderTemplate($obj, $controller);
|
||||||
|
|
||||||
|
$result = ArrayList::create();
|
||||||
|
$result->push($obj);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getURL($args)
|
||||||
|
{
|
||||||
|
$singleFieldName = static::config()->get('single_field_name');
|
||||||
|
$listFieldName = static::config()->get('list_field_name');
|
||||||
|
|
||||||
|
$filterLink = $args['filter'][$singleFieldName] ?? ($args['filter'][$listFieldName] ?? null);
|
||||||
|
$argLink = $args[$singleFieldName] ?? ($args[$listFieldName] ?? null);
|
||||||
|
|
||||||
|
$linkData = $filterLink ?: $argLink;
|
||||||
|
if (!$linkData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $linkData;
|
||||||
|
if ($url === '/') {
|
||||||
|
return '/home';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getURLController($url)
|
||||||
|
{
|
||||||
|
$curr = Controller::curr();
|
||||||
|
$req = clone $curr->getRequest();
|
||||||
|
|
||||||
|
$req->setUrl($url);
|
||||||
|
$req->match('$URLSegment//$Action/$ID/$OtherID', true);
|
||||||
|
|
||||||
|
$controller = ModelAsController::create();
|
||||||
|
$controller->setRequest($req);
|
||||||
|
|
||||||
|
// ContentController
|
||||||
|
$result = $controller->getNestedController();
|
||||||
|
$result->setRequest($req);
|
||||||
|
|
||||||
|
/** @var SiteTree $child */
|
||||||
|
$action = $req->param('Action');
|
||||||
|
|
||||||
|
if ($action) {
|
||||||
|
$child = self::findChild($action, $result);
|
||||||
|
if ($child) {
|
||||||
|
$result = $child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look recursively for a child page with URLSegment
|
||||||
|
protected static function findChild($action, $controller)
|
||||||
|
{
|
||||||
|
$req = $controller->getRequest();
|
||||||
|
|
||||||
|
$child = SiteTree::get()->filter([
|
||||||
|
'ParentID' => $controller->ID,
|
||||||
|
'URLSegment' => $action,
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
if ($child) {
|
||||||
|
$req->shiftAllParams();
|
||||||
|
$req->shift();
|
||||||
|
|
||||||
|
$controller = ModelAsController::controller_for($child);
|
||||||
|
$controller->setRequest($req);
|
||||||
|
$action = $req->param('Action');
|
||||||
|
|
||||||
|
if ($action) {
|
||||||
|
return self::findChild($action, $controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AJAX/GraphQL helper
|
||||||
|
protected static function RenderTemplate($page, $ctl)
|
||||||
|
{
|
||||||
|
$object = $page;
|
||||||
|
$req = $ctl->getRequest();
|
||||||
|
$actionParam = $req->param('Action');
|
||||||
|
|
||||||
|
Director::set_current_page($object);
|
||||||
|
|
||||||
|
$match = self::findAction($ctl, $req);
|
||||||
|
$req->match($match['rule'], true);
|
||||||
|
$action = $match['action'];
|
||||||
|
|
||||||
|
$action = ($action === 'handleAction') ? $actionParam : $action;
|
||||||
|
|
||||||
|
$action = $action && $ctl->hasAction($action) ? $action : 'index';
|
||||||
|
|
||||||
|
|
||||||
|
// find templates
|
||||||
|
$tpl = 'Page';
|
||||||
|
$tpls = SSViewer::get_templates_by_class($object->ClassName, '', \Page::class);
|
||||||
|
|
||||||
|
foreach ($tpls as $tpl) {
|
||||||
|
if (is_array($tpl)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$a_tpl = explode('\\', $tpl);
|
||||||
|
$last_name = array_pop($a_tpl);
|
||||||
|
$a_tpl[] = 'Layout';
|
||||||
|
$a_tpl[] = $last_name;
|
||||||
|
$a_tpl = implode('\\', $a_tpl);
|
||||||
|
|
||||||
|
if (SSViewer::hasTemplate($a_tpl)) {
|
||||||
|
$tpl = $a_tpl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
$tpl = is_array($tpl) ? 'Page' : $tpl;
|
||||||
|
$tpl = ($tpl !== 'Page') ? $tpl : 'Layout/Page';
|
||||||
|
|
||||||
|
// a little dirty way to make forms working
|
||||||
|
Controller::curr()->config()->set('url_segment', $object->AbsoluteLink());
|
||||||
|
$ctl->doInit();
|
||||||
|
|
||||||
|
$mResult = $ctl->$action($req);
|
||||||
|
if (is_array($mResult) || is_a($mResult, ArrayData::class)) {
|
||||||
|
$ctl->customise($mResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$layout = $ctl->renderWith([$tpl.'_'.$action, $tpl]);
|
||||||
|
|
||||||
|
return $ctl
|
||||||
|
->customise(['Layout' => $layout])
|
||||||
|
->renderWith('GraphQLPage');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function findAction($controller, $request)
|
||||||
|
{
|
||||||
|
$handlerClass = $controller::class;
|
||||||
|
|
||||||
|
// We stop after RequestHandler; in other words, at ViewableData
|
||||||
|
while ($handlerClass && $handlerClass != ViewableData::class) {
|
||||||
|
$urlHandlers = Config::inst()->get($handlerClass, 'url_handlers', Config::UNINHERITED);
|
||||||
|
|
||||||
|
if ($urlHandlers) {
|
||||||
|
foreach ($urlHandlers as $rule => $action) {
|
||||||
|
if (isset($_REQUEST['debug_request'])) {
|
||||||
|
$class = static::class;
|
||||||
|
$remaining = $request->remaining();
|
||||||
|
Debug::message("Testing '{$rule}' with '{$remaining}' on {$class}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->match($rule, true)) {
|
||||||
|
if (isset($_REQUEST['debug_request'])) {
|
||||||
|
$class = static::class;
|
||||||
|
$latestParams = var_export($request->latestParams(), true);
|
||||||
|
Debug::message(
|
||||||
|
"Rule '{$rule}' matched to action '{$action}' on {$class}. " . "Latest request params: {$latestParams}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'rule' => $rule,
|
||||||
|
'action' => $action,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$handlerClass = get_parent_class($handlerClass ?? '');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -36,9 +36,21 @@ class DeferredRequirements implements TemplateGlobalProvider
|
|||||||
'DeferedJS' => 'loadJS',
|
'DeferedJS' => 'loadJS',
|
||||||
'WebpackActive' => 'webpackActive',
|
'WebpackActive' => 'webpackActive',
|
||||||
'EmptyImgSrc' => 'emptyImageSrc',
|
'EmptyImgSrc' => 'emptyImageSrc',
|
||||||
|
'HttpMethod' => 'httpMethod',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function httpMethod(): string
|
||||||
|
{
|
||||||
|
$ctl = Controller::curr();
|
||||||
|
if (!$ctl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$req = $ctl->getRequest();
|
||||||
|
return ($req) ? $req->httpMethod() : null;
|
||||||
|
}
|
||||||
|
|
||||||
public static function Auto($class = false): string
|
public static function Auto($class = false): string
|
||||||
{
|
{
|
||||||
$config = Config::inst()->get(self::class);
|
$config = Config::inst()->get(self::class);
|
||||||
|
Loading…
Reference in New Issue
Block a user