zauberfisch/silverstripe-easy-linkfield

SilverStripe inline link field that allows adding one or multiple links to any object and saves into a single DB field

Installs: 1 034

Dependents: 0

Suggesters: 0

Security: 0

Stars: 2

Watchers: 4

Forks: 0

Open Issues: 1

Type:silverstripe-vendormodule

v1.0.6 2023-05-10 22:48 UTC

This package is auto-updated.

Last update: 2024-04-11 01:03:36 UTC


README

Allows adding one or multiple links to any object and saves into a single DB field. Editing happens inline in the form field, no GridField or popup is used.

Screenshots

Empty LinkList:

LinkList with 6 links (all possible types):

Know Bugs/Limitations (fixes are being worked on)

  • At the moment, there is no way to limit the amount of links a user can add
  • Uploading a file only works in the popup for attaching a file. the direct "upload new" link is not working (1)
  • Using normal DropdownField with indentation instead of TreeDropdownField for Page selection (1)
  • Files are not automatically published

(1) Problem is a bug in the underlying dependency. Sub-Routes in ArrayListField are currently not working

Maintainer Contact

Requirements

  • php >=7.1
  • silverstripe/framework >=4.5
  • zauberfisch/silverstripe-serialized-dataobject >=4

Installation

  • composer require "zauberfisch/silverstripe-easy-linkfield"
  • rebuild manifest (flush)

Documentation

<?php

class Page extends SilverStripe\CMS\Model\SiteTree {
    private static $db = [
        'Buttons' => \zauberfisch\LinkField\DBLinkList::class,
    ];

    public function getCMSFields() {
        $fields = parent::getCMSFields();
        $fields->addFieldsToTab( 'Root.Main', [
            (new \zauberfisch\LinkField\LinkListField( 'Buttons', 'My Buttons'))
                ->setOrderable(true),
        ]);
        return $fields;
    }
}

Restrict link types

You can also limit the types of links that are allowed (builtin link types are: 'internal', 'external', 'file', 'email', 'phone'):

<?php

class MyClass extends \SilverStripe\ORM\DataObject {
    private static $db = [
        'ContactDetails' => \zauberfisch\LinkField\DBLinkList::class,
    ];

    public function getCMSFields() {
        $fields = parent::getCMSFields();
        $fields->addFieldsToTab( 'Root.Main', [
            (new \zauberfisch\LinkField\LinkListField( 'ContactDetails', 'My Contact Details', ['email', 'phone']))
                ->setOrderable(true),
        ]);
        return $fields;
    }
}

Accessing the values in php

$list = $page->obj('Buttons')->getValue(); // $page being a Page object with a field Buttons from the example above
foreach($list as $button) {
    /** @var \zauberfisch\LinkField\Link\AbstractLink $button */
    // Always available Variables: getLink(), getAbsoluteLink(), getLinkType(), getTitle(), getNewTab()
    // And depending on the type: getPage() (internal), getPageID() (internal), getURL() (external), getFile() (file), getFileID() (file), getEmail() (email), getCountryPrefix() (phone), getNumber() (phone), getPhoneNumber() (phone)
    $link = $button->getLink();
    $absoluteLink = $button->getAbsoluteLink();
    $type = $button->getLinkType(); // one of 'internal', 'external', 'file', 'email', 'phone'
    $title = $button->getTitle();
    $openInNewTab = $button->getNewTab();
    // use the values here
}

Accessing the values in a template

<% loop $Buttons.getValue %>
    <%-- Always available Variables: $Link, $AbsoluteLink, $LinkType, $Title, $NewTab --%>
    <%-- And depending on the type: $Page (internal), $PageID (internal), $URL (external), $File (file), $FileID (file), $Email (email), $CountryPrefix (phone), $Number (phone), $PhoneNumber (phone) --%>
    <%-- If you use fields depending on the type, you have to check for the type first, otherwise you will get an error that the field was not found --%>
    <%-- For example <% if $LinkType == 'internal' %>The Link is $Link and the PAGE URLSegment is $Page.URLSegment<% end_if %> --%>
    <a href="$Link" <% if $NewTab %>target="_blank"<% end_if %>>$Title</a>
<% end_loop %>

Creating custom link type (eg for a DataObject)

<?php

declare(strict_types=1);

namespace app\model\shop;

class Product extends DataObject {
  public function Link() { return "/shop/product-{$this->ID}/"; }
  public function AbsoluteLink() { return \SilverStripe\Control\DIrector::absoluteURL($this->Link()); }
}
# /app/_config/extensions.yml
zauberfisch\LinkField\LinkListField:
  link_types:
    product: 'app\model\ProductLink'
# /app/src/model/ProductLink.php
<?php

declare(strict_types=1);

namespace app\model;

use app\shop\Product;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use zauberfisch\LinkField\Link\AbstractLink;

/**
 * @property string $ProductID
 * @method string|int getProductID()
 * @method static setProductID(int $productID)
 */
class ProductLink extends AbstractLink {
	private static $fields = [
		'ProductID',
	];

	public function getCMSFields(): FieldList {
		$fields = parent::getCMSFields();
		$fields->insertBefore(
			'NewTab',
			new DropdownField('ProductID', $this->fieldLabel('Product'), Product::get()->map()->toArray())
		);
		return $fields;
	}

	/**
	 * @return \SilverStripe\ORM\DataObject|null|Product
	 */
	public function getProduct(): ?Product {
		return $this->getProductID() ? Product::get()->byID($this->getProductID()) : null;
	}
	
	/**
	 * @return \SilverStripe\ORM\DataObject|null|Product
	 */
	public function Product(): ?Product {
		return $this->getProduct();
	}

	public function getLink(): string {
		$product = $this->getProduct();
		return $product && $product->exists() ? $product->Link() : '';
	}

	public function getAbsoluteLink(): string {
		$product = $this->getProduct();
		return $product && $product->exists() ? $product->AbsoluteLink() : '';
	}

	public function getLinkType(): string {
		return 'product';
	}
}