Filtering in Silverstripe GridFieldAddExistingAutocompleter - silverstripe

Is it possible to add a filter to GridFieldAddExistingAutocompleter similar setFilterFunction on TreeDropdownField?
TreeDropdownField::create(
"LinkID",
"Link to",
"SiteTree"
)->setFilterFunction(create_function( '$obj', 'return $obj->isPublished();' ));
This would give a list of SiteTree objects that return true when isPublished is called.
Is there a way to filter the GridField autocompleter in a similar way?
In the following code $this->Pages() is a has_many relationship for Page. The result will just list all of the Page objects in the autocomplete dropdown.
GridField::create(
'Pages',
'Pages belonging to group',
$this->Pages(),
$config = GridFieldConfig::create()->addComponents(
new GridFieldButtonRow('toolbar-header-right'),
new GridFieldToolbarHeader(),
new GridFieldSortableHeader(),
new GridFieldFilterHeader(),
new GridFieldDataColumns(),
new GridFieldDeleteAction(true),
new GridFieldDetailForm(),
new GridFieldAddExistingAutocompleter('toolbar-header-right')
)
)
Is there a way it can be filtered?
Note: I want to filter by some other checks not just isPublished, I'm checking if the page's parent ID is the same as another relationship's ID

there is a method called setSearchList() on GridFieldAddExistingAutocompleter with which you can set the list to be searched.
however, I am not sure if that is fully implemented, but you should try the following:
// lets assume you want all Pages that have no parent (root pages)
$listToBeSearched = Page::get()->filter('ParentID', 0);
$autoCompleteField = new GridFieldAddExistingAutocompleter('toolbar-header-right');
$autoCompleteField->setSearchList($listToBeSearched);
$config = GridFieldConfig::create()->addComponents(
new GridFieldButtonRow('toolbar-header-right'),
new GridFieldToolbarHeader(),
new GridFieldSortableHeader(),
new GridFieldFilterHeader(),
new GridFieldDataColumns(),
new GridFieldDeleteAction(true),
new GridFieldDetailForm(),
$autoCompleteField
);
if you wish to filter by a function like in your question, then use filterByCallback() to filter the list:
// because GridFieldAddExistingAutocompleter only supports DataList at the moment
// and filterByCallback() returns an ArrayList, we have to use a workaround
// (which obviously yields a performance loss)
$arrayList = Page::get()->filterByCallback(function($obj) { return $obj->isPublished(); });
// now that we have a list of pages that we want to search,
// lets take the IDs of this list and create a DataList that filters for this IDs:
$listToBeSearched = Page::get()->filter('ID', $arrayList->column('ID'));

Related

Drupal 7, save new node while editing old

My need is to change behavior of edit form, for several content types.
The objective is:
-After update button has been pressed, don't update the node but create a new one with values from old node. I could do that by passing old node's fields values to "/node/add/my_content" form but this require a lot of work (the forms are quite complicated) and on edit page i have already all values ready in my fields.
So i already tried hook_form_alter
function mymodule_form_alter (&$form, &$form_state, $form_id) {
switch ($form_id) {
case 'my_not_update_form':
$node = $form_state['node'];
if (!isset($node->nid) || isset($node->is_new)) {
// This is a new node.
}
else {
$new_node = new StdClass();
$new_node->type = 'my_not_update_form';
node_object_prepare($new_node);
$new_node->uid = $user->uid;
$new_node->language = 'und';
$new_node->title = NULL;
$form['vid']['#value'] = NULL;
$form['nid']['#value'] = NULL;
$form['created']['#value'] = $new_node->created;
$form['changed']['#default_value'] = NULL;
$form['#node'] = $new_node;
$form['#entity'] = $new_node;
$form_state['node'] = $new_node;
$form_state['build_info']['args'][0] = $new_node;
}
break;
}
}
So with the above code i'm able to create a new node but the "create date" parameter always stay the same as create date parameter of an old node and none of the above line can solve that problem.
If you want to create an entirely new node when you submit edits to an existing node, then you want to use hook_node_presave(), which allows you to set any property of the node object before it's saved to the database.
In this example unsetting the nid and vid, and explicitly setting the is_new property will achieve this:
function my_module_node_presave($node) {
unset($node->nid);
unset($node->vid);
$node->is_new = TRUE;
}
This will leave the existing node untouched and unedited, and will instead create an entirely new node.
So to fully change the behavior of form update i give up on hook_form_alter() and instead i used hook_node_presave
function mymodule_node_presave($node) {
if($node->is_new == FALSE || isset($node->nid)) {
unset($node->nid);
unset($node->vid);
unset($node->vuuid);
$node -> created = time();
$node -> timestamp = time();
$node-> is_new = TRUE;
$node -> changed = time();
unset($node->revision_timestamp);
unset($node->num_revisions);
unset($node->current_revision_id);
unset($node->is_current);
unset($node->is_pending);
unset($node->revision_moderation);
unset($node->date);
unset($node->vuuid);
unset($node->data);
}
}

Flex 3: dynamically created checkbox column in datagrid - data population issues and event listener

I have a datagrid control in my mxml file:
Now in my AS file, in the result function when obtaining data from DB, I can create columns dynamically. Let's say I create 1 column (client name):
private function GetDebtors_Result(event:ResultEvent):void
{
var arrayCol:Array = new Array();
var xmlSrc:XML = new XML("<main></main>");
var xmlTmp:XML;
var colClientname:DataGridColumn;
//Build an XML from DB data received (could as well use "event.result" directly to act as dataprovider for the datagrid, but I needed to break it down here)
for each(var o:Object in event.result)
{
xmlTmp = <row>
<CLIENTNAME>{o.CLIENTNAME}</CLIENTNAME>
</row>;
xmlSrc.appendChild(xmlTmp);
}
//Create the column CLIENTNAME
colClientname = new DataGridColumn("CLIENTNAME");
colClientname.headerText = "Client Name";
//Add the newly created column in the "Column" array.
arrayCol.push(colClientname);
//Use the "Column" array to set the columns of the datagrid.
dgSearch.columns = arrayCol;
//Populate the datagrid with the XML data.
dgSearch.dataProvider = xmlSrc.row;
}
This works well.
Now comes the issue: I need to add a second column which will contain checkboxes. They will be selected or deselected depending on the data from database. I'll show how I've done it by updating the same "GetDebtors_Result" function as above (added lines are commented as "// ADDED"):
private function GetDebtors_Result(event:ResultEvent):void
{
var arrayCol:Array = new Array();
var xmlSrc:XML = new XML("<main></main>");
var xmlTmp:XML;
var colClientname:DataGridColumn;
var colSel:DataGridColumn; // **ADDED**
//Build an XML from DB data received (could as well use "event.result" directly to act as dataprovider for the datagrid, but I needed to break it down here)
for each(var o:Object in event.result)
{
xmlTmp = <row>
<CLIENTNAME>{o.CLIENTNAME}</CLIENTNAME>
<SELECTED>{(o.SELECTED == 1)?true:false}</SELECTED> //**ADDED**
</row>;
xmlSrc.appendChild(xmlTmp);
}
//Create the column CLIENTNAME
colClientname = new DataGridColumn("CLIENTNAME");
colClientname.headerText = "Client Name";
//Create the column SELECTED
colSel = new DataGridColumn("SELECTED"); // **ADDED**
colSel.headerText = ""; // **ADDED**
colSel.itemRenderer = new ClassFactory(mx.controls.CheckBox); // **ADDED**
colSel.dataField = "SELECTED"; // **ADDED**
//Add the newly created column in the "Column" array.
arrayCol.push(colClientname);
//Add the "selection" column in the "Column" array.
arrayCol.push(colSel); // **ADDED**
//Use the "Column" array to set the columns of the datagrid.
dgSearch.columns = arrayCol;
//Populate the datagrid with the XML data.
dgSearch.dataProvider = xmlSrc.row;
}
Problem #1: The checkbox column appears, I can check and uncheck the checkboxes, but they are not checked/unchecked respective to DB data when loaded.
Problem #2: How do I associate a function to the checkboxes, for instance one which will update the XML so that I can save the new data to the DB?
Anybody got the solution? Thank you in advance.
Seems to be a very old question that I saw today.
Hopefully you would have found out the solution by now, just in-case if anyone has same problem:
While adding a checkbox to column- just instantiate it 1st:
var chkTempCheck: Checkbox = new CheckBox();
Then set all the properties required:
chkTempCheck.selected = o.dBColumnToDecideCheckUnCheck
here 'o' is the Object you are using from event.result.
This will work for sure!
The initial scenario was: all columns were defined in the mxml file. The checkbox column used itemrenderer and was working properly. I was using the same datagrid in 3 different cases - only thing was that some columns were set visible/invisible depending on the 'views'. The problem was when shifting 'views' and populate the grid and shift 'views' again, the column widths kept increasing exponentially. I excluded the checkbox column and everything worked fine; columns widths were ok. I included the checkbox column back and tried setting the column widths in AS file and the column-increasing-exponentially problem was fixed but the column widths were never the same when populating grid in view A and when populating grid in view B. ...So I ventured out in trying to set the columns in AS file just after obtaining DB data. Hope you can find yourself in those situations. Thanks for helping.

Populate TreeView object in ASP.NET

I am new to TreeView in asp.net so any help regarding populating nodes would be great.
I have a DataTable that gets populated by a SQLAdapter. It contains:
-House- -Region- -Division-
Aurora House Arizona Central
Copper Hills Arizona Central
Arbor House DFW Central
Angelina House Timberland West
I want to know how to place data or create a TreeView that would look similar to this:
Selection
Corporate
Division
Region
House(s)
I have spent a lot of time researching about how to do this and I just cant wrap my head around it I dont know if its my data that it is in DataTable that makes it harder to do.
This is all going to be based on if the user has access to that particular Corporate/Division/Region/House so it I would like to have TreeView for admin show everything but for others show only what they have access to.
I dont think that is going to be a problem as long as I get the SQL that populates the Data Table correct. My concern also is the duplicate Regions/Divisions how do I also have them only appear once and not multiple times?
Thank you for all your help.
Nick
Basically, you'd ideally like to have an object (well, collection) that mimics the hierarchy that you want to present with the treeview.
I'm assuming the list that comes from the database is as you presented it in your question, and I'm assuming that "Selection" column contains
Maybe you can use Linq to create such hierarchical collection. Or maybe you don't even need to create that collection - maybe you can just go ahead and start building the treeview as you iterate your data records using - for example - linq and some looping.
There may be some more elegant ways, but something like this should work:
var selections = dt.Select(r => Convert.ToString(r["Selection"])).Distinct().ToList();
foreach (string selection in selections)
{
// create treeview node here; add to treeview
object selectionNode = null; // change type to treeview node type
var corporateList = dt.Where(r => Convert.ToString(r["Selection"]) == selection).Select(r => Convert.ToString(r["Corporate"])).Distinct().ToList();
foreach (string corporate in corporateList)
{
// create treeview node here; add it as a child to selectionNode
object corporateNode = null; // change type to tv node
var divisions = dt.Where(r => Convert.ToString(r["Selection"]) == selection && Convert.ToString(r["Corporate"]) == corporate).Select(r => Convert.ToString(r["Division"])).Distinct().ToList();
foreach (string div in divisions)
{
// create treeview node here; add it as a child to corporateNode
}
}
}
You can dynamically create a TreeView Control like this:
using (var db = new FORMS())
{
//Get Chapters from selected form
var query = from b in db.CHAPTERS
select b;
//Create treeview hierarchy
foreach (var rootItem in query)
{
TreeNode myNode = new TreeNode(rootItem.titulo, rootItem.id.ToString());
var childQuery = from b in db.SECTIONS
where b.idChapter = rootItem.id
select b;
//Add childs
foreach (var childItem in childQuery)
{
TreeNode myChildNode = new TreeNode(childItem.titulo, childItem.id.ToString());
myNode.ChildNodes.Add(myChildNode);
}
ChapterTreeView.Nodes.Add(myNode);
}
}
In this case I only have two levels for my TreeView Control, but you can nest as many as you want.

HierarchicalCollectionView: One time sort?

I have an AdvancedDataGrid that relies on a HierarchicalCollectionView as it's dataProvider. What I'd like to do is sort the data when it is first loaded, but then disable the sort so that anything that is added after the initial load doesn't cause the grid to auto-sort again. I tried something like this:
this._myDataProvider = new HierarchicalCollectionView(
new HierarchicalData(this._model.rootTasks));
var mySort:Sort = new Sort();
mySort.fields = [new SortField("startDate")];
this._tasksDataProvider.sort = taskSorting;
this._tasksDataProvider.refresh();
this._tasksDataProvider.sort = null;
But setting the sort to null just leaves the data unsorted. I guess what I'm asking is: how can I sort the underlying hierarchical data since it seems setting the sort property will keep it dynamically sorting. Thanks for any help you can provide.
Personally, I would change the sort order when you're getting the data. Either it's done on the server side or when you parse the data (ie. in your model). You can do a one time sort using Array with sortOn.
you can
1. sort the original data with sort function,
2. clone content and put it to a new collection with no sort (be careful do and make a manual clone),
3. just use the new data collection.
I had the same kind of problem until I realized that the sorting with Sort object does not change the "physical" ordering of the items within the Collection, so when you remove the Sort, the next refresh reverts the view to the actual "physical" ordering.
Similarily as stated above, I solved by it by cloning the sub-collections into sorted order this way:
public static function buildPositionSort():Sort
{
var dataSortField:SortField = new SortField();
dataSortField.name = "position";
dataSortField.numeric = true;
dataSortField.descending = false;
var sort:Sort = new Sort();
sort.fields = [ dataSortField ];
return sort;
}
/**
* This method is used to create a clone of ArrayCollection, because sorting does not
* actually change the physical ordering of the items.
*/
public static function createSortedArrayCollectionCopy(source:ArrayCollection):ArrayCollection
{
var target:ArrayCollection = new ArrayCollection();
source.sort = buildPositionSort();
source.refresh();
for each (var item:Object in source)
{
if (item.children != null) item.children = createSortedArrayCollectionCopy(item.children);
target.addItem(item);
}
return target;
}

Issue with LINQ to SQL insert . .

i was looking at an example of how to do an insert in Linq to SQL and here it was it said:
NorthwindDataContext context = new NorthwindDataContext();
context.Products.Add(new Product(..));
context.SubmitChanges();
but when i look at the below, (in my case the Table is UserInfo), the Table doesn't have an "Add" method:
public System.Data.Linq.Table<UserInfo> UserInfos
{
get
{
return this.GetTable<UserInfo>();
}
}
any clue what i am doing wrong here?
You should use the InsertOnSubmit method:
NorthwindDataContext context = new NorthwindDataContext();
context.Products.InsertOnSubmit(new Product(..));
context.SubmitChanges();
The Add method exist on the EntitySet members, is mostly used when adding Child entities to a Parent one, for example:
var category = new Category{ Name = "Breveages"};
category.Products.Add(new Product{ Name = "Orange Juice"});
category.Products.Add(new Product{ Name = "Tomato Juice"});
category.Products.Add(new Product{ Name = "Cola"});
//...
context.Categories.InsertOnSubmit(category);
// This will insert the Category and
// the three Products we associated to.
EDIT: To do update operations, you just need to retrieve the entity by doing a query, or attaching it, for example:
var customer = context.Customers.Single( c => c.CustomerID == "ALFKI");
customer.ContactName = "New Contact Name";
context.SubmitChanges();
The DataContext tracks the changes of its related entities and when the SubmitChanges method is called, it will detect that change, and generate an Update SQL statement behind the scenes to do the update operation...

Resources