diff --git a/src/ActionsGridFieldItemRequest.php b/src/ActionsGridFieldItemRequest.php
index b7c7f72..2004497 100644
--- a/src/ActionsGridFieldItemRequest.php
+++ b/src/ActionsGridFieldItemRequest.php
@@ -3,6 +3,7 @@
 namespace LeKoala\CmsActions;
 
 use Exception;
+use PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Exp;
 use ReflectionMethod;
 use SilverStripe\Forms\Tab;
 use SilverStripe\Forms\Form;
@@ -152,6 +153,7 @@ public function recordCmsUtils()
         $reflectionMethod = new ReflectionMethod($owner, 'getRecord');
         $record = count($reflectionMethod->getParameters()) > 0 ? $owner->getRecord(0) : $owner->getRecord();
         if ($record && $record->hasMethod('getCMSUtils')) {
+            //@phpstan-ignore-next-line
             $utils = $record->getCMSUtils();
             $this->extend('onCMSUtils', $utils, $record);
             $record->extend('onCMSUtils', $utils);
@@ -160,9 +162,38 @@ public function recordCmsUtils()
         return false;
     }
 
+    /**
+     * @param Form $form
+     * @return void
+     */
+    public function updateItemEditForm($form)
+    {
+        $record = $this->owner->getRecord();
+        if (!$record) {
+            return;
+        }
+
+        // We get the actions as defined on our record
+        /** @var FieldList $CMSActions */
+        $CMSActions = $record->getCMSActions();
+
+        $FormActions = $form->Actions();
+
+        // Push our actions that are otherwise ignored by SilverStripe
+        foreach ($CMSActions as $CMSAction) {
+            $action = $FormActions->fieldByName($CMSAction->getName());
+
+            if ($action) {
+                // If it has been made readonly, revert
+                if ($CMSAction->isReadonly() != $action->isReadonly()) {
+                    $FormActions->replaceField($action->getName(), $action->setReadonly($CMSAction->isReadonly()));
+                }
+            }
+        }
+    }
+
     /**
      * Called by GridField_ItemRequest
-     * GridField_ItemRequest defines its own set of actions so we need to add ours
      * We add our custom save&close, save&next and other tweaks
      * Actions can be made readonly after this extension point
      * @param FieldList $actions
@@ -546,7 +577,7 @@ protected function getBtnClassForRecord(DataObject $record)
     /**
      * @param string $action
      * @param array<FormField>|FieldList $definedActions
-     * @return mixed|CompositeField|null
+     * @return FormField|null
      */
     protected static function findAction($action, $definedActions)
     {
@@ -632,6 +663,14 @@ protected function forwardActionToRecord($action, $data = [], $form = null)
                 $availableActions
             ));
         }
+
+        if ($clickedAction->isReadonly() || $clickedAction->isDisabled()) {
+            return $this->owner->httpError(403, sprintf(
+                'Action %s is disabled',
+                $clickedAction->getName(),
+            ));
+        }
+
         $message = null;
         $error = false;
 
@@ -639,6 +678,11 @@ protected function forwardActionToRecord($action, $data = [], $form = null)
         // It can be deleted by the action, and it will return to the list
         $isNewRecord = $record->ID === 0;
 
+        $actionTitle = $clickedAction->getName();
+        if (method_exists($clickedAction, 'getTitle')) {
+            $actionTitle = $clickedAction->getTitle();
+        }
+
         try {
             $result = $record->$action($data, $form, $controller);
 
@@ -653,7 +697,10 @@ protected function forwardActionToRecord($action, $data = [], $form = null)
                 $message = _t(
                     'ActionsGridFieldItemRequest.FAILED',
                     'Action {action} failed on {name}',
-                    ['action' => $clickedAction->getTitle(), 'name' => $record->i18n_singular_name()]
+                    [
+                        'action' => $actionTitle,
+                        'name' => $record->i18n_singular_name(),
+                    ]
                 );
             } elseif (is_string($result)) {
                 // Result is a message
@@ -670,7 +717,10 @@ protected function forwardActionToRecord($action, $data = [], $form = null)
             $message = _t(
                 'ActionsGridFieldItemRequest.DONE',
                 'Action {action} was done on {name}',
-                ['action' => $clickedAction->getTitle(), 'name' => $record->i18n_singular_name()]
+                [
+                    'action' => $actionTitle,
+                    'name' => $record->i18n_singular_name(),
+                ]
             );
         }
         $status = 'good';
@@ -822,7 +872,7 @@ public function doSaveAndClose($data, $form)
      * @param string $dir prev|next
      * @param array<string,mixed> $data The form data
      * @param Form|null $form
-     * @return void
+     * @return HTTPResponse
      */
     protected function doSaveAndAdjacent(string $dir, array $data, ?Form $form)
     {
@@ -837,6 +887,10 @@ protected function doSaveAndAdjacent(string $dir, array $data, ?Form $form)
             throw new Exception("Could not get class");
         }
 
+        if (!in_array($dir, ['prev', 'next'])) {
+            throw new Exception("Invalid dir $dir");
+        }
+
         $method = match ($dir) {
             'prev' => 'getCustomPreviousRecordID',
             'next' => 'getCustomNextRecordID',
diff --git a/src/CustomButton.php b/src/CustomButton.php
index d79bf61..9bc31d8 100644
--- a/src/CustomButton.php
+++ b/src/CustomButton.php
@@ -15,7 +15,8 @@ trait CustomButton
      * @var array
      */
     private static $default_classes = [
-        'btn', 'btn-info'
+        'btn',
+        'btn-info'
     ];
 
     /**
@@ -56,7 +57,7 @@ public function getTitle()
     /**
      * Set the value of title
      *
-     * @param string $is
+     * @param string $title
      * @return $this
      */
     public function setTitle($title)
@@ -206,6 +207,7 @@ public function renderLastIcon()
 
     /**
      * Get the value of confirmation
+     * @return bool|string
      */
     public function getConfirmation()
     {
@@ -215,7 +217,7 @@ public function getConfirmation()
     /**
      * Set the value of confirmation
      *
-     * @param string|bool A confirm message or true for a generic message
+     * @param string|bool $confirmation A confirm message or true for a generic message
      * @return $this
      */
     public function setConfirmation($confirmation)
diff --git a/src/DefaultLink.php b/src/DefaultLink.php
index 6066eea..de66717 100644
--- a/src/DefaultLink.php
+++ b/src/DefaultLink.php
@@ -93,6 +93,7 @@ public function getControllerLink($action, array $params = null)
 
     /**
      * Get the value of link
+     * @return string
      */
     public function getLink()
     {
@@ -102,6 +103,7 @@ public function getLink()
     /**
      * Set the value of link
      *
+     * @param string $link
      * @return $this
      */
     public function setLink($link)
@@ -113,6 +115,7 @@ public function setLink($link)
 
     /**
      * Get the value of newWindow
+     * @return bool
      */
     public function getNewWindow()
     {
@@ -122,6 +125,7 @@ public function getNewWindow()
     /**
      * Set the value of newWindow
      *
+     * @param bool $newWindow
      * @return $this
      */
     public function setNewWindow($newWindow)
diff --git a/src/GridFieldSaveAllButton.php b/src/GridFieldSaveAllButton.php
index 9b6a6ae..df32b80 100644
--- a/src/GridFieldSaveAllButton.php
+++ b/src/GridFieldSaveAllButton.php
@@ -5,7 +5,10 @@
 use SilverStripe\Control\Controller;
 use SilverStripe\Control\Director;
 use SilverStripe\Forms\GridField\GridField;
+use SilverStripe\ORM\DataList;
 use SilverStripe\ORM\DataObject;
+use Exception;
+use SilverStripe\Control\HTTPResponse;
 
 /**
  * When using inline editing on a ModelAdmin, there is no save button
@@ -25,12 +28,23 @@ class GridFieldSaveAllButton extends GridFieldTableButton
     protected $allowEmptyResponse = true;
     protected bool $shouldReload = false;
 
+    /**
+     * @param string $targetFragment
+     * @param mixed $buttonLabel
+     */
     public function __construct($targetFragment = 'buttons-before-left', $buttonLabel = null)
     {
         parent::__construct($targetFragment, $buttonLabel);
         $this->buttonLabel = $buttonLabel ?? _t('GridFieldSaveAllButton.SaveAll', 'Save all');
     }
 
+    /**
+     * @param GridField $gridField
+     * @param Controller $controller
+     * @param array $arguments
+     * @param array $data
+     * @return ?HTTPResponse
+     */
     public function handle(GridField $gridField, Controller $controller, $arguments = [], $data = [])
     {
         $fieldName = $gridField->getName();
@@ -40,9 +54,12 @@ public function handle(GridField $gridField, Controller $controller, $arguments
         // Without this, handleSave does not work
         $gridField->setSubmittedValue($data[$fieldName]);
 
+        if (!($list instanceof DataList)) {
+            throw new Exception("Requires a DataList");
+        }
+
         $updatedData = $data[$fieldName]['GridFieldEditableColumns'] ?? [];
         foreach ($updatedData as $id => $values) {
-            /** @var DataObject $record */
             $record = $list->byID($id);
             if (!$record) {
                 continue;
@@ -61,17 +78,17 @@ public function handle(GridField $gridField, Controller $controller, $arguments
         }
         $newData = $data[$fieldName]['GridFieldAddNewInlineButton'] ?? [];
         foreach ($newData as $idx => $values) {
+            $record = new $model;
             if ($this->useHandleSave) {
                 /** @var \Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton $component */
                 $component = $gridField->getConfig()->getComponentByType(\Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton::class);
                 $component->handleSave($gridField, $record);
             } else {
-                $record = new $model;
                 foreach ($values as $k => $v) {
                     $record->$k = $v;
                 }
-                $record->write();
             }
+            $record->write();
         }
 
         $response = $controller->getResponse();
diff --git a/src/ProgressiveAction.php b/src/ProgressiveAction.php
index 55ef36c..036eda5 100644
--- a/src/ProgressiveAction.php
+++ b/src/ProgressiveAction.php
@@ -4,6 +4,9 @@
 
 trait ProgressiveAction
 {
+    /**
+     * @var bool
+     */
     protected $progressive = false;
 
     /**