Skip to content

Commit

Permalink
ENH Save relations on link creation
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Dec 20, 2023
1 parent 4e6c662 commit 3800716
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 75 deletions.
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

29 changes: 25 additions & 4 deletions client/src/components/LinkField/LinkField.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable */
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, createContext } from 'react';
import { bindActionCreators, compose } from 'redux';
import { connect } from 'react-redux';
import { injectGraphql } from 'lib/Injector';
Expand All @@ -14,6 +14,8 @@ import Config from 'lib/Config';
import PropTypes from 'prop-types';
import i18n from 'i18n';

export const LinkFieldContext = createContext(null);

// section used in window.ss config
const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController';

Expand All @@ -24,7 +26,16 @@ const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController';
* actions - object of redux actions
* isMulti - whether this field handles multiple links or not
*/
const LinkField = ({ value = null, onChange, types = [], actions, isMulti = false }) => {
const LinkField = ({
value = null,
onChange,
types = [],
actions,
isMulti = false,
ownerID,
ownerClassName,
ownerRelation,
}) => {
const [data, setData] = useState({});
const [editingID, setEditingID] = useState(0);

Expand Down Expand Up @@ -153,7 +164,11 @@ const LinkField = ({ value = null, onChange, types = [], actions, isMulti = fals
const renderPicker = isMulti || Object.keys(data).length === 0;
const renderModal = Boolean(editingID);

return <>
return <LinkFieldContext.Provider value={{
ownerID,
ownerClassName,
ownerRelation,
}}>
{ renderPicker && <LinkPicker onModalSuccess={onModalSuccess} onModalClosed={onModalClosed} types={types} /> }
<div> { renderLinks() } </div>
{ renderModal && <LinkModalContainer
Expand All @@ -163,9 +178,12 @@ const LinkField = ({ value = null, onChange, types = [], actions, isMulti = fals
onSuccess={onModalSuccess}
onClosed={onModalClosed}
linkID={editingID}
ownerID={ownerID}
ownerClassName={ownerClassName}
ownerRelation={ownerRelation}
/>
}
</>;
</LinkFieldContext.Provider>;
};

LinkField.propTypes = {
Expand All @@ -174,6 +192,9 @@ LinkField.propTypes = {
types: PropTypes.objectOf(LinkType).isRequired,
actions: PropTypes.object.isRequired,
isMulti: PropTypes.bool,
ownerID: PropTypes.number.isRequired,
ownerClassName: PropTypes.string.isRequired,
ownerRelation: PropTypes.string.isRequired,
};

// redux actions loaded into props - used to get toast notifications
Expand Down
9 changes: 7 additions & 2 deletions client/src/components/LinkModal/LinkModal.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable */
import React from 'react';
import React, { useContext } from 'react'
import FormBuilderModal from 'components/FormBuilderModal/FormBuilderModal';
import { LinkFieldContext } from 'components/LinkField/LinkField';
import url from 'url';
import qs from 'qs';
import Config from 'lib/Config';
Expand All @@ -11,13 +12,17 @@ const buildSchemaUrl = (typeKey, linkID) => {
const parsedURL = url.parse(schemaUrl);
const parsedQs = qs.parse(parsedURL.query);
parsedQs.typeKey = typeKey;
const { ownerID, ownerClassName, ownerRelation } = useContext(LinkFieldContext)
parsedQs.ownerID = ownerID;
parsedQs.ownerClassName = ownerClassName;
parsedQs.ownerRelation = ownerRelation;
for (const prop of ['href', 'path', 'pathname']) {
parsedURL[prop] = `${parsedURL[prop]}/${linkID}`;
}
return url.format({ ...parsedURL, search: qs.stringify(parsedQs)});
}

const LinkModal = ({ typeTitle, typeKey, linkID = 0, isOpen, onSuccess, onClosed}) => {
const LinkModal = ({ typeTitle, typeKey, linkID = 0, isOpen, onSuccess, onClosed }) => {
if (!typeKey) {
return false;
}
Expand Down
7 changes: 5 additions & 2 deletions client/src/entwine/LinkField.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ jQuery.entwine('ss', ($) => {
* @returns {Object}
*/
getProps() {
const value = this.getInputField().data('value');
const inputField = this.getInputField();
return {
value,
value: inputField.data('value'),
ownerID: inputField.data('owner-id'),
ownerClassName: inputField.data('owner-class-name'),
ownerRelation: inputField.data('owner-relation'),
onChange: this.handleChange.bind(this),
isMulti: this.data('is-multi') ?? false,
types: this.data('types') ?? [],
Expand Down
8 changes: 8 additions & 0 deletions src/Controllers/LinkFieldController.php
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ private function createLinkForm(Link $link, string $operation): Form
/** @var Form $form */
$form = $formFactory->getForm($this, $name, ['Record' => $link]);

// Explicity add hidden form fields for OwnerID, OwnerClassName and OwnerRelation
if ($operation === 'create') {
// TODO: need to pass this values for stuff in via $_GET first
$form->Fields()->push(HiddenField::create('OwnerID'));
$form->Fields()->push(HiddenField::create('OwnerClassName'));
$form->Fields()->push(HiddenField::create('OwnerRelation'));
}

// Set where the form is submitted to
$typeKey = LinkTypeService::create()->keyByClassName($link->ClassName);
$form->setFormAction($this->Link("linkForm/$id?typeKey=$typeKey"));
Expand Down
64 changes: 39 additions & 25 deletions src/Form/LinkField.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@
namespace SilverStripe\LinkField\Form;

use LogicException;
use SilverStripe\CMS\Controllers\CMSPageEditController;
use SilverStripe\CMS\Controllers\ContentController;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\FormField;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\LinkField\Models\Link;
use SilverStripe\LinkField\Form\Traits\AllowedLinkClassesTrait;
use SilverStripe\LinkField\Form\Traits\OwnerFieldsTrait;

/**
* Allows CMS users to edit a Link object.
*/
class LinkField extends FormField
{
use AllowedLinkClassesTrait;
use OwnerFieldsTrait;

protected $schemaComponent = 'LinkField';

Expand All @@ -31,46 +36,55 @@ public function setValue($value, $data = null)
return parent::setValue($id, $data);
}

/**
* @param DataObject|DataObjectInterface $record - A DataObject such as a Page
* @return $this
*/
public function saveInto(DataObjectInterface $record)
{
// Check required relation details are available
$fieldname = $this->getName();
if (!$fieldname) {
throw new LogicException('LinkField must have a name');
}
// /**
// * @param DataObject|DataObjectInterface $record - A DataObject such as a Page
// * @return $this
// */
// public function saveInto(DataObjectInterface $record)
// {
// // Check required relation details are available
// $fieldname = $this->getName();
// if (!$fieldname) {
// throw new LogicException('LinkField must have a name');
// }

$linkID = $this->dataValue();
$dbColumn = $fieldname . 'ID';
$record->$dbColumn = $linkID;
// $linkID = $this->dataValue();
// $dbColumn = $fieldname . 'ID';
// $record->$dbColumn = $linkID;

// Store the record as the owner of the link.
// Required for permission checks, etc.
$link = Link::get()->byID($linkID);
if ($link) {
$link->OwnerID = $record->ID;
$link->OwnerClass = $record->ClassName;
$link->OwnerRelation = $fieldname;
$link->write();
}
// // Store the record as the owner of the link.
// // Required for permission checks, etc.
// $link = Link::get()->byID($linkID);
// if ($link) {
// $link->OwnerID = $record->ID;
// $link->OwnerClass = $record->ClassName;
// $link->OwnerRelation = $fieldname;
// $link->write();
// }

return $this;
}
// return $this;
// }

protected function getDefaultAttributes(): array
{
$attributes = parent::getDefaultAttributes();
$attributes['data-value'] = $this->Value();
$ownerFields = $this->getOwnerFields();
$attributes['data-owner-id'] = $ownerFields['ID'];
$attributes['data-owner-class-name'] = $ownerFields['ClassName'];
$attributes['data-owner-relation'] = $ownerFields['Relation'];
return $attributes;
}

public function getSchemaDataDefaults()
{
$data = parent::getSchemaDataDefaults();
$data['types'] = json_decode($this->getTypesProps());
// TODO In LinkField this is in getSchemaStateDefaults() - is that wrong?
$ownerFields = $this->getOwnerFields();
$data['ownerID'] = $ownerFields['ID'];
$data['ownerClassName'] = $ownerFields['ClassName'];
$data['ownerRelation'] = $ownerFields['Relation'];
return $data;
}
}
47 changes: 29 additions & 18 deletions src/Form/MultiLinkField.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use LogicException;
use SilverStripe\Forms\FormField;
use SilverStripe\LinkField\Form\Traits\AllowedLinkClassesTrait;
use SilverStripe\LinkField\Form\Traits\OwnerFieldsTrait;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\Relation;
Expand All @@ -18,6 +19,7 @@
class MultiLinkField extends FormField
{
use AllowedLinkClassesTrait;
use OwnerFieldsTrait;

protected $schemaComponent = 'LinkField';

Expand All @@ -38,27 +40,27 @@ public function setValue($value, $data = null)
return parent::setValue($ids, $data);
}

public function saveInto(DataObjectInterface $record)
{
$fieldName = $this->getName();
if (!$fieldName) {
throw new LogicException('LinkField must have a name');
}
// public function saveInto(DataObjectInterface $record)
// {
// $fieldName = $this->getName();
// if (!$fieldName) {
// throw new LogicException('LinkField must have a name');
// }

$relation = $record->hasMethod($fieldName) ? $record->$fieldName() : null;
if (!$relation) {
throw new LogicException("{$record->ClassName} is missing the relation '$fieldName'");
}
// $relation = $record->hasMethod($fieldName) ? $record->$fieldName() : null;
// if (!$relation) {
// throw new LogicException("{$record->ClassName} is missing the relation '$fieldName'");
// }

// Use RelationList rather than Relation here since some Relation classes don't allow setting value - but RelationList does.
if (!($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
throw new LogicException("'$fieldName()' method on {$record->ClassName} doesn't return a relation list");
} else {
$relation->setByIDList($this->getValueArray() ?? []);
}
// // Use RelationList rather than Relation here since some Relation classes don't allow setting value - but RelationList does.
// if (!($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
// throw new LogicException("'$fieldName()' method on {$record->ClassName} doesn't return a relation list");
// } else {
// $relation->setByIDList($this->getValueArray() ?? []);
// }

return $this;
}
// return $this;
// }

public function getSchemaDataDefaults()
{
Expand All @@ -72,13 +74,22 @@ public function getSchemaStateDefaults()
{
$data = parent::getSchemaStateDefaults();
$data['value'] = $this->getValueArray();
// TODO In LinkField this is in getSchemaDataDefaults() - is that wrong?
$ownerFields = $this->getOwnerFields();
$data['ownerID'] = $ownerFields['ID'];
$data['ownerClassName'] = $ownerFields['ClassName'];
$data['ownerRelation'] = $ownerFields['Relation'];
return $data;
}

protected function getDefaultAttributes(): array
{
$attributes = parent::getDefaultAttributes();
$attributes['data-value'] = $this->getValueArray();
$ownerFields = $this->getOwnerFields();
$attributes['data-owner-id'] = $ownerFields['ID'];
$attributes['data-owner-class-name'] = $ownerFields['ClassName'];
$attributes['data-owner-relation'] = $ownerFields['Relation'];
return $attributes;
}

Expand Down
35 changes: 35 additions & 0 deletions src/Form/Traits/OwnerFieldsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace SilverStripe\LinkField\Form\Traits;

use SilverStripe\CMS\Controllers\ContentController;
use SilverStripe\CMS\Controllers\CMSPageEditController;
use SilverStripe\CMS\Model\SiteTree;
use LogicException;

trait OwnerFieldsTrait
{
private function getOwnerFields()
{
$form = $this->getForm();
$controller = $form->getController();
$owner = null;
if (is_a($controller, ContentController::class)) {
// TODO: confirm if this codepath is used for anything
// TODO confirm if the below is correct
$owner = $controller->data();
} elseif (is_a($controller, CMSPageEditController::class)) {
// TODO: this code path is not used for elemental ... it's
$pageID = (int) $controller->getRequest()->param('ID');
$owner = SiteTree::get()->byID($pageID);
}
if (!$owner) {
throw new LogicException('Could not determine owner');
}
return [
'ID' => $owner->ID,
'ClassName' => $owner::class,
'Relation' => $this->getName(), // TODO confirm
];
}
}
Loading

0 comments on commit 3800716

Please sign in to comment.