Skip to content

Commit

Permalink
NEW Add versioning to Link
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Dec 5, 2023
1 parent 3c8edfd commit fe2b73b
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 22 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,22 @@ class Page extends SiteTree
];

private static $has_many = [
'HasManyLinks' => Link::class
'HasManyLinks' => Link::class,
];

private static array $owns = [
'HasOneLink',
'HasManyLinks',
];

private static array $cascade_deletess = [
'HasOneLink',
'HasManyLinks',
];

private static array $cascade_duplicatess = [
'HasOneLink',
'HasManyLinks',
];

public function getCMSFields()
Expand Down
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/dist/styles/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions client/src/components/LinkField/LinkField.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,15 @@ const LinkField = ({ value = null, onChange, types, actions, isMulti = false })
continue;
}

console.log(data[linkID]?.versionState);

const type = types.hasOwnProperty(data[linkID]?.typeKey) ? types[data[linkID]?.typeKey] : {};
links.push(<LinkPickerTitle
key={linkID}
id={linkID}
title={data[linkID]?.Title}
description={data[linkID]?.description}
versionState={data[linkID]?.versionState}
typeTitle={type.title || ''}
onClear={onClear}
onClick={() => { setEditingID(linkID); }}
Expand Down
30 changes: 30 additions & 0 deletions client/src/components/LinkPicker/LinkPicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
text-align: left;
margin-right: 0;
justify-content: space-between;
position: relative;

&:not(:last-child) {
border-bottom: 0;
Expand All @@ -63,6 +64,35 @@
text-decoration: none;
color: inherit;
}

// version-state icon
&::before {
top: 29px;
left: 32px;
content: ' ';
position: absolute;
border: 1px solid $state-draft;
border-radius: 100%;
bottom: 6px;
box-shadow: 0 0 1px .5px $white;
display: block;
height: 8px;
width: 8px;
z-index: 1;
}

&--draft::before {
background-color: $state-draft-bg;;
}

&--modified::before {
background-color: $state-modified-bg;
}

&--unsaved::before,
&--published::before {
display: none;
}
}

.link-picker__button {
Expand Down
16 changes: 13 additions & 3 deletions client/src/components/LinkPicker/LinkPickerTitle.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable */
import classnames from 'classnames';
import i18n from 'i18n';
import React from 'react';
import PropTypes from 'prop-types';
Expand All @@ -12,8 +13,16 @@ const stopPropagation = (fn) => (e) => {
fn && fn();
}

const LinkPickerTitle = ({ id, title, description, typeTitle, onClear, onClick }) => (
<div className={classnames('link-picker__link', 'form-control')}>
const LinkPickerTitle = ({ id, title, description, versionState, typeTitle, onClear, onClick }) => {
const classes = {
'link-picker__link': true,
'form-control': true,
};
if (versionState) {
classes[` link-picker__link--${versionState}`] = true;
}
const className = classnames(classes);
return <div className={className}>
<Button className="link-picker__button font-icon-link" color="secondary" onClick={stopPropagation(onClick)}>
<div className="link-picker__link-detail">
<div className="link-picker__title">{title}</div>
Expand All @@ -25,12 +34,13 @@ const LinkPickerTitle = ({ id, title, description, typeTitle, onClear, onClick }
</Button>
<Button className="link-picker__clear" color="link" onClick={stopPropagation(() => onClear(id))}>{i18n._t('LinkField.CLEAR', 'Clear')}</Button>
</div>
);
};

LinkPickerTitle.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string,
description: PropTypes.string,
versionState: PropTypes.string,
typeTitle: PropTypes.string.isRequired,
onClear: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"type": "silverstripe-vendormodule",
"require": {
"php": "^8.1",
"silverstripe/cms": "^5"
"silverstripe/cms": "^5",
"silverstripe/versioned": "^2"
},
"require-dev": {
"silverstripe/recipe-testing": "^3",
Expand Down
21 changes: 21 additions & 0 deletions src/Controllers/LinkFieldController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\HiddenField;
use SilverStripe\ORM\DataList;
use SilverStripe\Versioned\Versioned;

class LinkFieldController extends LeftAndMain
{
Expand Down Expand Up @@ -113,6 +114,7 @@ private function getLinkData(Link $link): array
}
$data = $link->jsonSerialize();
$data['description'] = $link->getDescription();
$data['versionState'] = $this->getVersionedState($link);
return $data;
}

Expand Down Expand Up @@ -343,6 +345,25 @@ private function itemIDsFromRequest(): array
return $idsAsInt;
}

private function getVersionedState(Link $link): string
{
if (!$link->exists()) {
return 'unsaved';
}
if (Link::singleton()->hasExtension(Versioned::class)) {
if ($link->isModifiedOnDraft()) {
return 'modified';
}
if ($link->isPublished()) {
return 'published';
}
return 'draft';
}
// Unversioned - links are saved in the modal so there is no 'dirty state' and
// when undversioned saved is the same thing as published
return 'published';
}

/**
* Get the ?typeKey request querystring param
*/
Expand Down
11 changes: 10 additions & 1 deletion src/Models/Link.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use InvalidArgumentException;
use ReflectionException;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\CompositeValidator;
use SilverStripe\Forms\DropdownField;
Expand All @@ -14,6 +13,7 @@
use SilverStripe\LinkField\Type\Registry;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\Versioned\Versioned;

/**
* A Link Data Object. This class should be a subclass, and you should never directly interact with a plain Link
Expand All @@ -31,6 +31,15 @@ class Link extends DataObject
'OpenInNew' => 'Boolean',
];

// don't merge this, this will get added in a different card
private static array $has_one = [
'Parent' => DataObject::class,
];

private static array $extensions = [
Versioned::class,
];

/**
* In-memory only property used to change link type
* This case is relevant for CMS edit form which doesn't use React driven UI
Expand Down
18 changes: 4 additions & 14 deletions tests/php/Models/LinkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ protected function setUp(): void
$image = $this->objFromFixture(Image::class, 'image-1');
$image->setFromLocalFile(dirname(dirname(dirname(__FILE__))) . '/resources/600x400.png');
$image->write();
$image->publishSingle();

/** @var SiteTree $page */
$page = $this->objFromFixture(SiteTree::class, 'page-1');
$page->publishSingle();
}

protected function tearDown(): void
Expand Down Expand Up @@ -250,14 +245,9 @@ public function linkTypeEnabledProvider(): array
*/
public function testGetUrl(string $identifier, string $class, string $expected): void
{
Versioned::withVersionedMode(function () use ($identifier, $class, $expected): void {
Versioned::set_stage(Versioned::LIVE);

/** @var Link $link */
$link = $this->objFromFixture($class, $identifier);

$this->assertSame($expected, $link->getURL(), 'We expect specific URL value');
});
/** @var Link $link */
$link = $this->objFromFixture($class, $identifier);
$this->assertSame($expected, $link->getURL(), 'We expect specific URL value');
}

public function linkUrlCasesDataProvider(): array
Expand Down Expand Up @@ -326,7 +316,7 @@ public function linkUrlCasesDataProvider(): array
'file link / with image' => [
'file-link-with-image',
FileLink::class,
'/assets/ImageTest/600x400.png',
'/assets/8cf6c65fa7/600x400.png',
],
'file link / no image' => [
'file-link-no-image',
Expand Down

0 comments on commit fe2b73b

Please sign in to comment.