silverstripe / silverstripe-linkfield

Silverstripe module for managing links
BSD 3-Clause "New" or "Revised" License
4 stars 13 forks source link

SPIKE Managed archiving of Links in Fluent #285

Open maxime-rainville opened 1 month ago

maxime-rainville commented 1 month ago

We've recently identified that managing Archiving Link in Fluent doesn't work ... or at least works in a very clumsy way.

See https://github.com/silverstripe/silverstripe-linkfield/pull/273#issuecomment-2046515727 for the full context.

Timebox

1 work day

Objectives

GuySartorelli commented 1 month ago

Current behaviour

Localisation behaviour and UX in gridfields

Localisation behaviour and UX in linkfields

Current badges/flags fluent uses

Possible solutions

CMS5:

Additional, but probably over-the-top options

CMS 6

mfendeksilverstripe commented 2 weeks ago

Hey @GuySartorelli thanks for documenting the options we have in tackling this challenge. I really like the idea of focusing on low hanging fruit first before committing too much effort into this.

From all the available options for CMS 5 I like the LiteralField option the best. This is based on the feedback from the client on our project as we actually ended up building something similar for asset admin (similar problem - React UI, no GridFields).

The main reason why this solution was picked is that it gives content authors to overview across all locales. Model deletion was specifically an issue as content authors were reporting that it doesn't work. This is due to the delete / archive button not communicating the information that there is no content to delete in this locale.

We found this solution also to be least invasive as we're mostly just adding new enhancements in instead of replacing existing behaviour.

Here are some details about our solution.

Locale badges widget

Screenshot 2024-06-19 at 12 00 16 PM

class LocalisedBadges extends DataExtension
{
    public function updateCMSFields(FieldList $fields): void
    {
        /** @var SiteTree $owner */
        $owner = $this->getOwner();

        $field = DatalessField::create('LocalisedBadges');
        $field->setTemplate('App\Forms\LocalisedBadges');
        $field->customise(LocalisedBadgesHelper::singleton()->getLocales($owner));
        $fields->unshift($field);
    }
}
class LocalisedBadgesHelper
{

    use Injectable;

    /*
     * Specific logic for deciding how the badges will be displayed on the frontend
     */
    public function getLocales(SiteTree|File $data, string $objectType = 'Page'): array
    {
        $nonExistingLocales = Locale::getLocales()->sort('Sort');
        $existingLocales = Locale::getLocales()->sort('Sort');

        $localeVersions = FluentHelper::getLocaleCodesByObjectInstance($data);

        if (count($localeVersions) > 0) {
            $nonExistingLocales = $nonExistingLocales->exclude('Locale', $localeVersions);
            $existingLocales = $existingLocales->filter('Locale', $localeVersions);
        } else {
            $existingLocales = ArrayList::create();
        }

        $badgeData = [
            'EditLink' => $data->CMSEditLink(),
            'Existing' => $existingLocales,
            'NonExisting' => $nonExistingLocales,
            'ObjectType' => $objectType,
        ];

        return $badgeData;
    }
}
<div class="locale-badges">
  <% if $Current || $Existing %>
    <div class="locale-badge-container">
      <p>Locales</p>
      <div>
        <% loop $Existing %>
          <a href="{$Up.EditLink}/?l={$Locale}"
           aria-label="{$Title}"
           class="badge <% if $CurrentLocale.ID == $ID %>badge-success<% else %>badge-info fluent-badge--localised<% end_if %>"
           data-balloon-pos="down"
           target="_top"
          >
            <% if $LocaleSuffix = 001 %>
              EN
            <% else %>
              $LocaleSuffix
            <% end_if %>
          </a>
        <% end_loop %>
      </div>
    </div>
  <% end_if %>
  <% if $NonExisting %>
    <div class="locale-badge-container">
      <p>{$ObjectType} doesn't exist in</p>
      <div>
        <% loop $NonExisting %>
          <a href="{$Up.EditLink}/?l={$Locale}"
            aria-label="{$Title}"
            class="badge badge-secondary <% if $CurrentLocale.ID == $ID %>badge-success<% end_if %>"
            data-balloon-pos="down"
            target="_top"
            >
            <% if $LocaleSuffix = 001 %>
              EN
            <% else %>
              $LocaleSuffix
            <% end_if %>
          </a>
        <% end_loop %>
      </div>
    </div>
  <% end_if %>
</div>
.locale-badges {
  background-color: #E9F0F4;
  border-bottom: 1px solid #ced5e1;
  display: flex;
  margin: -20px -20px 20px;
  padding: 0 20px;

  [aria-label][data-balloon-pos]:after {
    font-family: inherit;
    text-transform: capitalize;
  }
}

.badge-non-existing-current {
  background-color: #333;

  &:hover {
    background-color: #333;
  }
}

.locale-badge-container {
  padding: 8px 0;

  p {
    margin-bottom: 0;
  }

  &:nth-of-type(n + 2) {
    margin-left: 15px;
    border-left: 1px solid #ced5e1;
    padding-left: 15px;
  }
}

// Asset admin editor locale badges
.editor__details {
  .locale-badges {
    margin-top: 0.5rem;
  }
}

Card fluent flag

Screenshot 2024-06-19 at 12 00 35 PM

public static function resolveInheritStatus(File $object, array $args, array $context, ?ResolveInfo $info): bool
{
    // just for code linting
    /** @var DataObject|Versioned|FluentVersionedExtension $versionedObject */
    $versionedObject = $object;

    return !$versionedObject->isDraftedInLocale();
}

This flag is then injected into the React component.

const inheritBadge = {
  node: "span",
  key: "inherit-status",
  className: `gallery-item--inherit ${styles.flagInheritedLocale}`,
};