Skip to content

Commit

Permalink
update for ss4/ss5
Browse files Browse the repository at this point in the history
  • Loading branch information
lekoala committed Oct 28, 2024
1 parent 41b090e commit 20d49b0
Show file tree
Hide file tree
Showing 19 changed files with 371 additions and 279 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: CI

on:
push:
pull_request:
workflow_dispatch:

jobs:
ci:
name: CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'restruct') || (github.event_name != 'schedule')
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
10
73 changes: 11 additions & 62 deletions .scrutinizer.yml
Original file line number Diff line number Diff line change
@@ -1,69 +1,18 @@
inherit: true

build:
image: default-bionic
environment:
php: 8.1.2
nodes:
analysis:
tests:
override: [php-scrutinizer-run]

checks:
php:
verify_property_names: true
verify_argument_usable_as_reference: true
verify_access_scope_valid: true
useless_calls: true
use_statement_alias_conflict: true
variable_existence: true
unused_variables: true
unused_properties: true
unused_parameters: true
unused_methods: true
unreachable_code: true
too_many_arguments: true
sql_injection_vulnerabilities: true
simplify_boolean_return: true
side_effects_or_types: true
security_vulnerabilities: true
return_doc_comments: true
return_doc_comment_if_not_inferrable: true
require_scope_for_properties: true
require_scope_for_methods: true
require_php_tag_first: true
psr2_switch_declaration: true
psr2_class_declaration: true
property_assignments: true
prefer_while_loop_over_for_loop: true
precedence_mistakes: true
precedence_in_conditions: true
phpunit_assertions: true
php5_style_constructor: true
parse_doc_comments: true
parameter_non_unique: true
parameter_doc_comments: true
param_doc_comment_if_not_inferrable: true
optional_parameters_at_the_end: true
one_class_per_file: true
no_unnecessary_if: true
no_trailing_whitespace: true
no_property_on_interface: true
no_non_implemented_abstract_methods: true
no_error_suppression: true
no_duplicate_arguments: true
no_commented_out_code: true
newline_at_end_of_file: true
missing_arguments: true
method_calls_on_non_object: true
instanceof_class_exists: true
foreach_traversable: true
fix_line_ending: true
fix_doc_comments: true
duplication: true
deprecated_code_usage: true
deadlock_detection_in_loops: true
code_rating: true
closure_use_not_conflicting: true
catch_class_exists: true
blank_line_after_namespace_declaration: false
avoid_multiple_statements_on_same_line: true
avoid_duplicate_types: true
avoid_conflicting_incrementers: true
avoid_closing_tag: true
assignment_of_null_return: true
argument_type_checks: true
duplication: true

filter:
paths: [code/*, tests/*]
paths: [src/*, tests/*]
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,10 @@ What is it?

A decorator for form fields that manage object relationships, to allow adding a new object on the fly through a dialog window. It can handle has_one, has_many or many_many relationships. At the moment it has been tested / works on DropdownField, ListboxField and CheckboxSetField. It works both in the CMS and in the frontend. For frontend, [Select2Field or MultiSelect2Field](https://github.com/sheadawson/silverstripe-select2) are recommended.

Screenshot
--------

![Screenshot](https://raw.github.com/sheadawson/silverstripe-quickaddnew/master/images/screenshot.png)

Requirements
--------

SilverStripe 3
SilverStripe 4/5

Usage
--------
Expand All @@ -23,18 +18,26 @@ Firstly, when creating the form field, we need to create a closure that returns
We do this because later on, when the field is refreshed with the newly created Object ID as it's value, we need to use this function
Again to get up to date data for the source.

$source = function(){
return MyObject::get()->map()->toArray();
};
```php
$source = function(){
return MyObject::get()->map()->toArray();
};
```

Then we can create the form field, calling the closure as the source argument

$field = DropdownField::create('MyObjectID', 'My Object', $source());
```php
$field = DropdownField::create('MyObjectID', 'My Object', $source());
```

Next, we can tell the field to use and configure quickaddnew. The first parameter is the class name of the object that will be created. The second is the $source closure Note: See QuickAddNewExtension::useAddNew() for the list of configurations parameters available. These allow you to customise the fields and required fields (for validation) for the dialog. By default the object class's getAddNewFields() or getCMSFields() methods are used

$field->useAddNew('MyObject', $source);

```php
$field->useAddNew('MyObject', $source);
```

Add the field to your FieldList

$fields->addFieldToTab('Root.Main', $field);
```php
$fields->addFieldToTab('Root.Main', $field);
```
3 changes: 0 additions & 3 deletions _config.php

This file was deleted.

9 changes: 5 additions & 4 deletions _config/quickaddnew.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
Name: quickaddnew
After: "#corefieldtypes"
---
DropdownField:
SilverStripe\Forms\DropdownField:
extensions:
['QuickAddNewExtension']
ListboxField:
- 'QuickAddNewExtension'
SilverStripe\Forms\ListboxField:
extensions:
['QuickAddNewExtension']
- 'QuickAddNewExtension'
51 changes: 51 additions & 0 deletions client/css/quickaddnew.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.cms .quickaddnew-field>.middleColumn {
/* stop gaps between inline blocks */
font-size: 0;
}

.cms .quickaddnew-field .chzn-container,
.cms .quickaddnew-field .chosen-container {
max-width: min(437px, 60vw);
/* 512px - 75px */
}

.cms .quickaddnew-button {
position: relative;
top: 4px;
z-index: 0;
display: inline-block;
margin-left: 5px;
vertical-align: top;
}

.quickaddnew-dialog {
max-height: min(80vh, 600px);
overflow: auto;
}

/* Fixes jquery ui styles that are broken in ss 5 */
.ui-dialog .ui-dialog-titlebar-close {
width: 30px;
height: 30px;
background: none;
z-index: 9999;
right: -12px;
}

.ui-dialog .ui-dialog-titlebar-close:hover {
border: 0;
opacity: 0.8;
}

.ui-widget-header .ui-icon-closethick {
top: 8px;
left: 8px;
}

.ui-widget-header .ui-button-icon.ui-icon-closethick {
background-image: url('/_resources/vendor/silverstripe/admin/client/dist/images/sprite-sprites-32x32.png');
}

.ui-dialog-titlebar-close .ui-icon {
pointer-events: none;
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
138 changes: 138 additions & 0 deletions client/javascript/quickaddnew.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
jQuery.entwine("quickaddnew", function ($) {
var fieldSelector = ".field.quickaddnew-field .quickaddnew-field";

$(".quickaddnew-button").entwine({
onmatch: function () {
var self = this;
},

onclick: function () {
this.siblings(fieldSelector).showDialog();
return false;
},
});

$(fieldSelector).entwine({
Loading: null,
Dialog: null,
URL: null,
onmatch: function () {
var self = this;

//Check to see if quickaddnew has been bound to this field before, sometimes jQuery plugins like Select2
//will trigger a binding a second time that we don't want.
if ($(this).parents().children(".quickaddnew-button").length > 0) {
return;
}
// create add new button
var button = $("<button />")
.attr("type", "button")
.attr("href", "#")
.text(ss.i18n._t("QUICKADDNEW.AddNew"))
.addClass("quickaddnew-button ss-ui-button ss-ui-button-small btn btn-secondary")
.appendTo(self.parents("div:first"));

// create dialog
var dialog = $("<div />").addClass("quickaddnew-dialog").appendTo(self.parents("div:first"));

this.setDialog(dialog);

// set URL
var fieldName = this.attr("name");
if (this.hasClass("checkboxset")) {
fieldName = this.find("input:checkbox")
.attr("name")
.replace(/\[[0-9]+\]/g, "");
}

var action = this.parents("form").attr("action").split("?", 2); //add support for url parameters e.g. ?locale=en_US when using Translatable

var dialogHTMLURL = this.data("quickaddnew-action");
if (!dialogHTMLURL) {
// Fallback to default action
dialogHTMLURL = action[0] + "/field/" + fieldName + "/AddNewFormHTML";
}
if (action[1]) {
dialogHTMLURL += "?" + action[1];
}
dialogHTMLURL = dialogHTMLURL.replace(/[\[\]']+/g, "");
this.setURL(dialogHTMLURL);

// configure the dialog
this.getDialog()
.data("field", this)
.dialog({
autoOpen: false,
width: 600,
modal: true,
resizable: false,
title: this.data("dialog-title"),
position: { my: "center", at: "center", of: window },
});

// handle dialog form submission
this.getDialog().on("submit", "form", function () {
var dlg = self.getDialog().dialog(),
options = {};

var $submitButtons = $(this).find('input[type="submit"], button[type="submit"]');
$submitButtons.addClass("loading ui-state-disabled");

// if this is a multiselect field, send the existing values
// along with the form submission so they can be included in the
// replacement field
if (self.val() && typeof self.val() === "object") {
options.data = {
existing: self.val().join(","),
};
}

options.success = function (res) {
var $response = $(res);
if ($response.is(".field")) {
self.getDialog().empty().dialog("close");
var $newInput = $response.find(self[0].tagName);
// Replace <select> <option>'s rather than the entire HTML block
// to avoid JS hooks being lost on the frontend.
if ($newInput[0] && $newInput[0].tagName === "SELECT") {
self.html($newInput.children());

// Support legacy and new chosen
self.trigger("liszt:updated").trigger("chosen:updated");
// Support select2
self.trigger("change.select2");
} else {
self.parents(".field:first").replaceWith(res);
}
} else {
self.getDialog().html(res);
}
};
options.complete = function () {
$submitButtons.removeClass("loading ui-state-disabled");
};

$(this).ajaxSubmit(options);

return false;
});

this._super();
},

showDialog: function (url) {
var dlg = this.getDialog();
// Check to see we have a dialog, other jquery plugins like Select2 can get bound to by accident
if (dlg !== null) {
dlg.empty().dialog("open").parent().addClass("loading");

dlg.load(this.getURL(), function () {
dlg.parent().removeClass("loading");
// set focus to first input element
dlg.find("form :input:visible:enabled:first").focus();
});
}
this._super();
},
});
});
Loading

0 comments on commit 20d49b0

Please sign in to comment.