lekoala / silverstripe-cms-actions
向 SilverStripe 中的模型添加动作
Requires
- php: ^8.1
- silverstripe/framework: ^5
- silverstripe/recipe-plugin: ^2
- silverstripe/vendor-plugin: ^2
Requires (Dev)
- phpunit/phpunit: ^9.5
- silverstripe/admin: ^1.4 || ^2
- squizlabs/php_codesniffer: ^3.5
This package is auto-updated.
Last update: 2024-09-05 10:23:19 UTC
README
简介
对于那些怀念 betterbuttons 的人来说 :-). 因为说到底,在 SilverStripe 中添加自定义动作真的很痛苦。
它是如何工作的?
实际上非常简单。首先,我们通过我们的 ActionsGridFieldItemRequest
改进了 GridFieldItemRequest
。这很大程度上受到了 betterbuttons 模块的影响。多亏了这个扩展,我们在 getCMSActions
中定义的动作将正确显示。
然后,我们将我们的请求转发到模型(一个在 Member 上声明的按钮调用 ItemRequest 处理器,它将动作转发到 Member 模型)。
我们可以在两个函数中声明事物
- 在
getCMSActions
中作为动作:这些将显示在常规“保存”按钮旁边
- 在
getCMSUtils
中作为工具:这些将显示在标签旁边的右上角
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
。
您还可以将按钮放入下拉菜单中。
$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
容器中包围的“保存”/“保存并关闭”一样进行分组。
$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
配置标志来禁用
删除操作在右侧
说实话,我不知道是谁认为将删除按钮放在保存按钮旁边是个好主意,但我更愿意把它放在右侧。
此功能可以通过enable_delete_right
配置标志来禁用
前后支持
SilverStripe 4.4引入了更精细的前后记录UI。然而,它只允许导航,不支持“保存并下一页”或“上一页和下一页”,这在您逐行编辑记录时非常有用。
此功能可以通过enable_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'));
出于安全原因,操作必须在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::onBeforeUpdateCMSActions
和DataObject::onAfterUpdateCMSActions
来添加自己的按钮。这将在所有按钮定义之后调用。
例如,请参阅我的软删除模块
中是如何实现的。
标签页跟踪
此扩展还会跟踪在调用保存和下一个/上一个标签页时活动的标签页。这允许在行中编辑内容并保持相同的标签页。
当单击主标签页时,它还会更新url。这样,当您重新加载页面时,正确的标签页将重新打开。
这还允许通过链接针对具有给定标签页的特定页面。
配置文件和LeftAndMain扩展支持
由于我们在SilverStripe\Admin\LeftAndMain
上应用了扩展,因此updateCMSActions/getCMSActions中声明的动作在配置文件中可见。
这里的问题是updateItemEditForm
永远不会被调用(这仅在GridFieldDetailForm_ItemRequest
中调用,因此当您在GridField项中时...例如在ModelAdmin中)。
例如,这意味着您的动作在Profile控制器提供的'保存'按钮之前显示。目前,此模块通过一些CSS修复了这个问题。
渐进式动作
自版本1.2以来,此模块支持渐进式动作。渐进式动作是使用进度条显示正在发生什么的按钮。在底层,它转换为对同一处理函数的多个ajax调用,并传递以下post参数:
- progress_step: 当前步骤
- progress_total: 这可以是预先设置的或由处理函数提供
渐进式动作支持GridFieldTableButton
和CustomLink
。
以下是一个示例实现。动作需要返回一个包含以下键的数组:
- 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
完全隐藏按钮
赞助商
本模块由RESTRUCT友好赞助
兼容性
已在5.1版本上进行测试,但应在任何^5.1的项目上运行
维护者
LeKoala - thomas@lekoala.be