lekoala/silverstripe-cms-actions

向 SilverStripe 中的模型添加动作

资助包维护!
lekoala

安装次数: 129,620

依赖项: 11

建议者: 1

安全: 0

星星: 37

关注者: 7

分支: 13

公开问题: 1

类型:silverstripe-vendormodule

1.7.3 2024-07-05 10:01 UTC

README

Build Status Build Status scrutinizer Code coverage

Latest Stable Version Latest Unstable Version Total Downloads License Monthly Downloads Daily Downloads

简介

对于那些怀念 betterbuttons 的人来说 :-). 因为说到底,在 SilverStripe 中添加自定义动作真的很痛苦。

它是如何工作的?

实际上非常简单。首先,我们通过我们的 ActionsGridFieldItemRequest 改进了 GridFieldItemRequest。这很大程度上受到了 betterbuttons 模块的影响。多亏了这个扩展,我们在 getCMSActions 中定义的动作将正确显示。

然后,我们将我们的请求转发到模型(一个在 Member 上声明的按钮调用 ItemRequest 处理器,它将动作转发到 Member 模型)。

我们可以在两个函数中声明事物

  • getCMSActions 中作为动作:这些将显示在常规“保存”按钮旁边

custom action

  • getCMSUtils 中作为工具:这些将显示在标签旁边的右上角

cms utils

LeftAndMain 支持

此模块主要针对处理常规 DataObjects 上的动作。然而,为了支持页面上的动作,ActionsGridFieldItemRequest 也应用于 LeftAndMain。因此,在页面上定义的动作也应该可以正常工作。

添加您的按钮

动作

简单地在您的 DataObjects 上使用 getCMSActions 并为您的 DataObjects 添加新按钮!为此,只需推送新的动作。CustomAction 类负责调用您在 DataObject 上定义的动作。

在下面的示例中,我们调用 doCustomAction。返回的字符串作为通知显示。如果没有指定返回字符串,我们将显示通用消息“在 {record} 上完成动作 {action}”。

public function getCMSActions()
{
    $actions = parent::getCMSActions();

    $actions->push(new CustomAction("doCustomAction", "My custom action"));

    return $actions;
}

public function doCustomAction() {
    return 'Done!';
}

如果它抛出异常或返回一个假的布尔值,它将显示一个错误消息

public function doCustomAction() {
    throw new Exception("Show this error");
    return false;
}

自定义动作是按钮,通过 Ajax 提交。如果它改变了记录的状态,您可能需要刷新 UI,但请注意不要丢失任何未保存的数据。

$myAction->setShouldRefresh(true);

您还可以在动作后重定向到自定义 URL(例如,到主列表)

$fields->push($doRedirect = new CustomAction('doRedirect', 'Redirect'));
$doRedirect->setRedirectURL("/admin/my_section/my_model");

有时,您不希望按钮,而是链接。请使用 CustomLink。这对于下载 Excel 报告或 PDF 文件等非常有用。

public function getCMSActions()
{
    $actions = parent::getCMSActions();

    $actions->push($downloadExcelReport = new CustomLink('downloadExcelReport','Download report'));
    $downloadExcelReport->setButtonIcon(SilverStripeIcons::ICON_EXPORT);

    return $actions;
}

public function downloadExcelReport() {
    echo "This is the report";
    die();
}

请注意,我们使用了一个不太干净的 die 模式,但您完全可以返回一个 HTTPResponse 对象。

CustomLink 默认使用 Ajax 导航。您可以使用 setNoAjax(true) 来防止此操作。CustomLink 可以在新的窗口中打开链接。您可以使用 setNewWindow(true) 来启用此操作。CustomLink 默认调用与名称匹配的模型上的动作。但您真的可以将其指向任何东西,甚至是使用 setLink('https//www.silverstripe.org') 的外部链接。

确认动作

如果动作可能具有潜在的危险性或避免误点击,您可以使用 setConfirmation('Are you sure') 设置确认消息或简单地传递 true 以获取通用消息。

装饰 & 位置

您可以设置图标。请参阅 SilverStripeIcons 类以获取可用图标。我们使用基本的 silverstripe 图标。

$downloadExcelReport->setButtonIcon(SilverStripeIcons::ICON_EXPORT);

也支持 SilverStripe 的本地 setIcon

您还可以将按钮放入下拉菜单中。

Drop-up example

$myAction->setDropUp(true);

最后图标

您可以使用无限数量的图标,使用 last-icon

只需确保您包含了所需的 JavaScript(CSS 已默认包含,因为它有限且没有副作用)

Requirements::javascript("https://cdn.jsdelivr.net.cn/npm/last-icon@2/last-icon.min.js");

或使用 yml

SilverStripe\Admin\LeftAndMain:
  extra_requirements_js:
    - "https://cdn.jsdelivr.net.cn/npm/last-icon@2/last-icon.min.js"

并添加您选择的图标

$my_action->setLastIcon('star');

您可以传递额外的参数或直接传递一个数组。

分组

按钮可以像在ActionButtonsGroup容器中包围的“保存”/“保存并关闭”一样进行分组。

Grouping example

$groupedButtons = [
    CustomAction::create("doAction1", "Action1")
        ->addExtraClass('btn-outline-info')
        ->removeExtraClass('btn-info'),
    CustomAction::create("doAction2", "Action2")
        ->addExtraClass('btn-outline-info')
        ->removeExtraClass('btn-info'),
];
$btnGroup = ActionButtonsGroup::create($groupedButtons);
$actions->push($btnGroup);

Utils

在您的扩展中声明getCMSUtils或使用updateCMSUtils。这些实用工具将出现在标签旁边。它们非常适合提供一些额外信息或导航。我使用它们来添加快捷键、计时器、下拉菜单...

public function getCMSUtils()
{
    $fields = new FieldList();
    $fields->push(new LiteralField("LastLoginInfo", "Last login at " . $this->LastLogin));
    return $fields;
}

保存并关闭

添加一个默认的“保存并关闭”或“创建并关闭”按钮,以便快速添加DataObjects。

此功能可以通过enable_save_close配置标志来禁用

save and close

删除操作在右侧

说实话,我不知道是谁认为将删除按钮放在保存按钮旁边是个好主意,但我更愿意把它放在右侧。

此功能可以通过enable_delete_right配置标志来禁用

delete btn

前后支持

SilverStripe 4.4引入了更精细的前后记录UI。然而,它只允许导航,不支持“保存并下一页”或“上一页和下一页”,这在您逐行编辑记录时非常有用。

此功能可以通过enable_save_prev_next配置标志来禁用

save prev next

您还可以使用HasPrevNextUtils特性来在您的实用工具中添加导航。

按记录配置UI选项

而不是使用全局配置标志,您可以根据正在编辑的记录配置表单。

您的DataObject需要实现getCMSActionsOptions。此函数应返回一个具有以下键的数组

  • save_close: true/false
  • save_prev_next: true/false
  • delete_right: true/false

这将被用作默认全局选项,如果提供的话。

自定义前后记录

通过实现PrevRecord和NextRecord以及您的DataObject,您可以覆盖SilverStripe提供的默认逻辑。

前后记录可能难以管理,如果您有网格状态问题,使用嵌套网格字段等。使用PrevRecord和NextRecord提供了一种简单的方法,无需处理状态问题。

向GridField行添加操作

您可以通过扩展GridFieldRowButton来创建新的行操作

所有操作都将存储在一个新的“操作”列中,该列支持多个操作。例如,可以直接从GridField下载文件,如发票等。

例如,您可以这样做

class MyRowAction extends GridFieldRowButton
{
    protected $fontIcon = 'torso';

    public function getActionName()
    {
        return 'my_action';
    }

    public function getButtonLabel()
    {
        return 'My Action';
    }

    public function doHandle(GridField $gridField, $actionName, $arguments, $data)
    {
        $item = $gridField->getList()->byID($arguments['RecordID']);
        if (!$item) {
            return;
        }
        // Do something with item
        // Or maybe download a file...
        return Controller::curr()->redirectBack();
    }
}

并在您的ModelAdmin中这样使用它

public function getGridFieldFrom(Form $form)
{
    return $form->Fields()->dataFieldByName($this->getSanitisedModelClass());
}

public function getEditForm($id = null, $fields = null)
{
    $form = parent::getEditForm($id, $fields);

    $gridfield = $this->getGridFieldFrom($form);

    if ($this->modelClass == MyModel::class) {
        $gridfield->getConfig()->addComponent(new MyRowAction());
    }

    return $form;
}

向GridField添加链接

如果您不喜欢操作,您也可以向GridField添加链接。

它将再次添加到“操作”列中。

这就像上面描述的CustomLink一样,因此如果我们回到我们的报告示例,我们会得到这个

$gridfield->getConfig()->addComponent(new GridFieldCustomLink('downloadExcelReport', 'Download Report'));

gridfield row actions

出于安全原因,操作必须在getCMSActions中声明。未能这样做将返回一个有用的错误消息。如果您不想在详细表单中显示按钮,只需在它上设置d-none即可

$actions->push($downloadExcelReport = new CustomLink('downloadExcelReport', 'Download report'));
$downloadExcelReport->addExtraClass('d-none');
//or simply...
//$downloadExcelReport->setHidden();

向整个GridField添加按钮

这是使用GridFieldTableButton完成的

class MyGridButton extends GridFieldTableButton
{
    protected $buttonLabel = 'Do stuff';
    protected $fontIcon = 'do_stuff';

    public function handle(GridField $gridField, Controller $controller)
    {
    }
}

然后可以将此类添加为常规GridField组件

在getCMSFields中添加操作

如果您有很多操作,有时将其添加到您的cms字段中可能更有意义。我使用此方法提供需要上传的模板文件等。

这是使用CmsInlineFormAction类完成的。请注意,doCustomAction函数必须在您的控制器上声明,而不是在模型上。

这是因为我们未提交表单,因此我们未使用ActionsGridFieldItemRequest处理记录。

public function getCMSFields()
{
    $fields = parent::getCMSFields();

    $fields->addFieldToTab('Root.Actions', new CmsInlineFormAction('doCustomAction', 'Do this'));

    return $fields;
}

在您的管理类中

// don't forget to add me to allowed_actions as well
function doCustomAction()
{
    // do something here
    return $this->redirectBack();
}

使用内联操作进行帖子

您还可以使用CmsInlineFormAction将整个表单发送到另一个位置。

只需这样做

    $fields->addFieldToTab('Root.MyTab', $myAction = new CmsInlineFormAction("myAction", "My Action"));
    $myAction->setPost(true);

并将其添加到您的管理类中

    public function myAction()
    {
        $RecordID = $this->getRequest()->getVar("ID");

        $message = "My action done";
        $response = Controller::curr()->getResponse();
        $response->addHeader('X-Status', rawurlencode($message));

        return $response;
    }

这将触发保存动作(通过ajax)调用您的新端点,允许自定义行为和反馈信息。

显示消息而不是动作

如果某个动作不可用/可见,用户可能会想知道原因。显然,您可以选择显示禁用的按钮,但也可以显示消息而不是按钮。可以这样做:

$actions->push(LiteralField::create('MyCustomAction', "<span class=\"bb-align\">Action not available</span>"));

bb-align类确保文本与按钮正确对齐。

扩展支持

如果您的扩展依赖于此模块,您可以通过扩展钩子DataObject::onBeforeUpdateCMSActionsDataObject::onAfterUpdateCMSActions来添加自己的按钮。这将在所有按钮定义之后调用。

例如,请参阅我的软删除模块中是如何实现的。

标签页跟踪

此扩展还会跟踪在调用保存和下一个/上一个标签页时活动的标签页。这允许在行中编辑内容并保持相同的标签页。

当单击主标签页时,它还会更新url。这样,当您重新加载页面时,正确的标签页将重新打开。

这还允许通过链接针对具有给定标签页的特定页面。

配置文件和LeftAndMain扩展支持

由于我们在SilverStripe\Admin\LeftAndMain上应用了扩展,因此updateCMSActions/getCMSActions中声明的动作在配置文件中可见。

这里的问题是updateItemEditForm永远不会被调用(这仅在GridFieldDetailForm_ItemRequest中调用,因此当您在GridField项中时...例如在ModelAdmin中)。

例如,这意味着您的动作在Profile控制器提供的'保存'按钮之前显示。目前,此模块通过一些CSS修复了这个问题。

渐进式动作

自版本1.2以来,此模块支持渐进式动作。渐进式动作是使用进度条显示正在发生什么的按钮。在底层,它转换为对同一处理函数的多个ajax调用,并传递以下post参数:

  • progress_step: 当前步骤
  • progress_total: 这可以是预先设置的或由处理函数提供

progressive action

渐进式动作支持GridFieldTableButtonCustomLink

以下是一个示例实现。动作需要返回一个包含以下键的数组:

  • progress_step: 更新后的步骤。通常是+1。
  • progress_total: 记录总数。它应该只计算一次(在初始运行时),如果没有提供。
  • progress_id: 您可以返回一个唯一id,它将在每次调用中传递
  • reload: 我们是否在结束时重新加载?
  • message: 每次运行都可以显示一个短暂的通知,并带有特定文本
  • label: 结束标签(默认:完成)。
class MyProgressiveTableButton extends GridFieldTableButton
{
    public function __construct($targetFragment = "buttons-before-right", $buttonLabel = null)
    {
        $this->progressive = true;
        parent::__construct($targetFragment, $buttonLabel);
    }

    public function handle(GridField $gridField, Controller $controller)
    {
        $step = (int) $controller->getRequest()->postVar("progress_step");
        $total = (int) $controller->getRequest()->postVar("progress_total");
        if (!$total) {
            $total = $list->count();
        }
        $i = 0;
        $res = null;
        foreach ($list as $rec) {
            if ($i < $step) {
                $i++;
                continue;
            }
            $res = "Processed record $i";
            break;
        }
        $step++;
        return [
            'progress_step' => $step,
            'progress_total' => $total,
            'reload' => true,
            'message' => $res,
        ];
    }
}

模态动作

如果需要在单击按钮时从用户那里获取一些输入怎么办?例如,显示一个漂亮的文本区域的“发送消息”按钮?

这作为另一个模块的一部分得到解决:https://github.com/lekoala/silverstripe-pure-modal

它需要显示模态(在SilverStripe 4中设置模态有点麻烦,因此我创建了一个模块来使其变得容易得多)。当使用这两个模块时,很容易有打开模态的动作。

折叠图标

在移动视图中,图标会折叠。如果您有自己的按钮,可以添加btn-mobile-collapse类,以便它们执行相同的操作。如果您的按钮上设置了图标,这将默认添加。

您还可以使用btn-mobile-hidden完全隐藏按钮

btn mobile collapse

赞助商

本模块由RESTRUCT友好赞助

兼容性

已在5.1版本上进行测试,但应在任何^5.1的项目上运行

维护者

LeKoala - thomas@lekoala.be