Skip to content

Commit

Permalink
Merge branch 'craft-3' of https://github.com/verbb/vizy into craft-4
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
#	composer.json
#	src/base/Node.php
#	src/elements/Block.php
#	src/helpers/Nodes.php
#	src/nodes/VizyBlock.php
#	src/web/assets/field/dist/js/main.js
#	src/web/assets/mix-manifest.json
  • Loading branch information
engram-design committed Apr 13, 2022
2 parents f7ada7b + 9ddbb3b commit 9b60b63
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 23 deletions.
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@
- Now requires PHP `^8.0.2`.
- Now requires Craft `^4.0.0-beta.1`.

## 1.0.13 - 2022-04-13

### Added
- Add support for `limit`, `orderBy` and `where` arguments for GraphQL queries, when querying `nodes`.
- Add descriptions for all attributes for GraphQL.
- Add `vizyBlock.getCollapsed()`.
- Add `vizyBlock.id`.
- Add `Node::isEmpty()`.

### Changed
- Change field layout instruction text for Vizy field settings.
- GraphQL queries using `nodes` now only return enabled nodes.

### Fixed
- Fix `NodeCollection::isEmpty` not working correctly.

## 1.0.12 - 2022-03-17

### Fixed
- Fix nested node content being incorrectly stripped out due to HTML purifier.

## 1.0.11 - 2022-03-13

### Changed
- Improve node collection performance.
- Minor Vizy block performance improvements.

### Fixed
- Fix serializing nested Vizy fields not being arrays.
- Fix a potential XSS vulnerability, where HTML wasn’t correctly encoded.
- Fix an error when serializing nested Vizy fields, when generating search keywords.
- Fix rendering node collections in the control panel automatically when not needed.
- Fix Vizy Block nodes not rendering correctly for GraphQL queries.
- Fix an error when querying `nodes` or `rawNodes` for GraphQL queries.

## 1.0.10 - 2022-02-28

### Added
Expand Down
38 changes: 37 additions & 1 deletion docs/developers/graphql.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,40 @@ Or, you can use `renderHtml` to return the generated HTML, as determined by Craf
"renderHtml": "<p class=\"text-left\">The name <a href=\"https://en.wikipedia.org/wiki/Gin\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">gin</a> is a <strong>shortened</strong> form of the <em>older</em> English word genever.</p>"
}
}
```
```

## The `nodes` query
This query is used to query for nodes, similar to how we would normally [query nodes](docs:template-guides/querying-nodes#querying-nodes).

| Argument | Type | Description
| - | - | -
| `where`| `string` | Used to filter items based on params. This should be a JSON-encoded string.
| `limit`| `int` | Limit the number of nodes returned.
| `orderBy`| `string` | Return nodes ordered by a property.

### Where
Return all paragraph nodes, and no other node types. See [query nodes](docs:template-guides/querying-nodes#querying-nodes) for more examples of how to query. This must be a JSON-encoded string.

```json
nodes(where: "{ \"type\": \"paragraph\" }") {

}
```

### Limit
Return the first 2 nodes.

```json
nodes(limit: 2) {

}
```

### Order By
Return all nodes ordered by their type.

```json
nodes(orderBy: 'type DESC') {

}
```
3 changes: 3 additions & 0 deletions docs/developers/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Method | Description
--- | ---
`selfClosing()` | Whether this node has a self-closing tag.
`isDeleted()` | Whether this node has been deleted. Only applicable to Vizy Blocks.
`isEmpty()` | Whether the node is considered empty of content.
`getTag()` | Returns the HTML tag name and attributes the node should use for the HTML tag.
`getField()` | Returns the Vizy field model.
`getType()` | Returns the type of node this is.
Expand Down Expand Up @@ -200,6 +201,7 @@ These can be accessed via `attrs.values`.

Value | Description
--- | ---
`id` | The ID for the block.
`type` | The ID for the block type.
`typeEnabled` | Whether the block type is enabled or not.
`content.fields` | An array containing the field data.
Expand All @@ -212,5 +214,6 @@ Method | Description
`getBlockType()` | Return the block type object.
`getFieldLayout()` | Return the field layout object.
`getEnabled()` | Returns whether the block is enabled or not.
`getCollapsed()` | Returns whether the block is collapsed or not.
`getBlockTypeEnabled()` | Returns whether the block type is enabled or not.

5 changes: 5 additions & 0 deletions src/base/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ public function getEnabled(): bool
return true;
}

public function isEmpty()
{
return !($this->getContent() || $this->getText());
}

public function renderNode(): ?string
{
return Vizy::$plugin->getNodes()->renderNode($this);
Expand Down
4 changes: 4 additions & 0 deletions src/gql/interfaces/VizyBlockInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,31 @@ public static function getFieldDefinitions(): array
return Craft::$app->getGql()->prepareFieldDefinitions(array_merge(parent::getFieldDefinitions(), [
'enabled' => [
'name' => 'enabled',
'description' => 'Whether this Vizy block is enabled or not.',
'type' => Type::boolean(),
'resolve' => function($source, $arguments) {
return $source->getEnabled();
},
],
'collapsed' => [
'name' => 'collapsed',
'description' => 'Whether this Vizy block is collapsed or not.',
'type' => Type::boolean(),
'resolve' => function($source, $arguments) {
return $source->attrs['collapsed'] ?? false;
},
],
'blockTypeId' => [
'name' => 'blockTypeId',
'description' => 'The block type ID for this Vizy block.',
'type' => Type::string(),
'resolve' => function($source, $arguments) {
return $source->getBlockType()->id;
},
],
'blockTypeHandle' => [
'name' => 'blockTypeHandle',
'description' => 'The block type handle for this Vizy block.',
'type' => Type::string(),
'resolve' => function($source, $arguments) {
return $source->getBlockType()->handle;
Expand Down
8 changes: 8 additions & 0 deletions src/gql/interfaces/VizyNodeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,49 +51,57 @@ public static function getFieldDefinitions(): array
return Craft::$app->getGql()->prepareFieldDefinitions([
'type' => [
'name' => 'type',
'description' => 'The node type.',
'type' => Type::string(),
],
'tagName' => [
'name' => 'tagName',
'description' => 'The HTML tag used for this node.',
'type' => Type::string(),
],
'html' => [
'name' => 'html',
'description' => 'The rendered HTML for this node.',
'type' => Type::string(),
'resolve' => function($source) {
return $source->renderHtml();
},
],
'content' => [
'name' => 'content',
'description' => 'The content for this node.',
'type' => ArrayType::getType(),
'resolve' => function($source) {
return $source->rawNode['content'] ?? [];
},
],
'attrs' => [
'name' => 'attrs',
'description' => 'The attributes for this node.',
'type' => ArrayType::getType(),
'resolve' => function($source) {
return $source->rawNode['attrs'] ?? [];
},
],
'marks' => [
'name' => 'marks',
'description' => 'The nested marks for this node.',
'type' => ArrayType::getType(),
'resolve' => function($source) {
return $source->rawNode['marks'] ?? [];
},
],
'text' => [
'name' => 'text',
'description' => 'The textual content for this node.',
'type' => Type::string(),
'resolve' => function($source) {
return $source->rawNode['text'] ?? '';
},
],
'rawNode' => [
'name' => 'rawNode',
'description' => 'The raw JSON content for this node.',
'type' => ArrayType::getType(),
],
], self::getName());
Expand Down
32 changes: 30 additions & 2 deletions src/gql/types/NodeCollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

use verbb\vizy\gql\interfaces\VizyNodeInterface;

use Craft;
use craft\gql\base\ObjectType;
use craft\gql\GqlEntityRegistry;
use craft\helpers\Gql;
use craft\helpers\Json;

use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
Expand All @@ -30,20 +32,46 @@ public static function getType($context = null)
'fields' => [
'nodes' => [
'name' => 'nodes',
'description' => 'Query nodes for this node collection.',
'args' => [
'where' => [
'name' => 'where',
'type' => Type::string(),
'description' => 'Used to filter items based on params. This should be a JSON-encoded string.',
],
'limit' => [
'name' => 'limit',
'type' => Type::int(),
'description' => 'Limit the number of nodes returned.',
],
'orderBy' => [
'name' => 'orderBy',
'type' => Type::string(),
'description' => 'Return nodes ordered by a property.',
],
],
'type' => Type::listOf(VizyNodeInterface::getType($context)),
'resolve' => function($source) {
return $source->getNodes();
'resolve' => function($source, $arguments) {
if (isset($arguments['where'])) {
$arguments['where'] = Json::decode($arguments['where']);
}

$query = Craft::configure($source->query(), $arguments);

return $query->all();
},
],
'rawNodes' => [
'name' => 'rawNodes',
'description' => 'The raw JSON of nodes for this node collection.',
'type' => ArrayType::getType(),
'resolve' => function($source) {
return $source->getRawNodes();
},
],
'renderHtml' => [
'name' => 'renderHtml',
'description' => 'The rendered HTML of nodes for this node collection.',
'type' => Type::string(),
],
],
Expand Down
55 changes: 48 additions & 7 deletions src/helpers/Nodes.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@

use Craft;
use craft\helpers\Html;
use craft\helpers\HtmlPurifier;
use craft\helpers\StringHelper;
use craft\validators\HandleValidator;

use LitEmoji\LitEmoji;

use HTMLPurifier_Config;

class Nodes
{
// Static Methods
Expand Down Expand Up @@ -95,34 +98,72 @@ public static function parseRefTags($value, $siteId): array|string|null
return $value;
}

public static function serializeEmojis($rawNode): array
public static function serializeContent($rawNode)
{
$content = $rawNode['content'] ?? [];

foreach ($content as $key => $block) {
$text = $block['text'] ?? '';
$type = $block['type'] ?? '';

// We only want to modify simple nodes and their text content, not complicated
// nodes like VizyBlocks, which could mess things up as fields control their content.
$rawNode['content'][$key]['text'] = LitEmoji::unicodeToShortcode($text);
$text = $block['text'] ?? '';

// Serialize any emoji's
$text = LitEmoji::unicodeToShortcode($text);

// Escape any HTML tags used in the text. Maybe we're writing HTML in text?
$text = StringHelper::htmlEncode($text);

// Run anything else not caught in the above through purifier to be extra safe
$text = HtmlPurifier::process($text, self::purifierConfig());

$rawNode['content'][$key]['text'] = $text;

// If this is now an empty text node, remove it. Tiptap won't like it.
if ($rawNode['content'][$key]['text'] === '' && $type === 'text') {
unset($rawNode['content'][$key]);
}
}

return $rawNode;
}

public static function normalizeEmojis($rawNode): array
public static function normalizeContent($rawNode)
{
$content = $rawNode['content'] ?? [];

foreach ($content as $key => $block) {
$text = $block['text'] ?? '';

// We only want to modify simple nodes and their text content, not complicated
// nodes like VizyBlocks, which could mess things up as fields control their content.
$rawNode['content'][$key]['text'] = LitEmoji::shortcodeToUnicode($text);
$text = $block['text'] ?? '';

// Un-serialize any emoji's
$text = LitEmoji::shortcodeToUnicode($text);

$rawNode['content'][$key]['text'] = $text;
}

return $rawNode;
}

private static function purifierConfig(): HTMLPurifier_Config
{
$purifierConfig = HTMLPurifier_Config::createDefault();
$purifierConfig->autoFinalize = false;

$config = [
'Attr.AllowedFrameTargets' => ['_blank'],
'Attr.EnableID' => true,
'HTML.SafeIframe' => true,
'URI.SafeIframeRegexp' => '%^(https?:)?//(www\.youtube(-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%',
];

foreach ($config as $option => $value) {
$purifierConfig->set($option, $value);
}

return $purifierConfig;
}

}
Loading

0 comments on commit 9b60b63

Please sign in to comment.