I have been going through the gridfield class documentation here;
http://doc.silverstripe.org/framework/en/reference/grid-field
Here is the code in question. While it does display a grid-field it adds a button on each columns. How would I edit this code to not display the buttons? The buttons are links to a non-existent page.
Link to rendered page; http://www.silverstripe.org/assets/Uploads/Capture28.JPG
public function AllPages() {
$gridField = new GridField('pages', 'All pages', SiteTree::get());
$dataColumns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
$dataColumns->setDisplayFields(array(
'Title' => 'Title',
'URLSegment'=> 'URL',
'LastEdited' => 'Changed'
));
return new Form($this, "AllPages", new FieldList($gridField), new FieldList());
}
The cause:
The SilverStripe GridField is pretty well built.
The Basic GridField has pretty much no features at all. Its just a plain table containing the data you want.
All other functionality is added by so called "Components" which are managed by the GridFieldConfig.
When you create a GridField like you did, without specifying a config, it will Create a config for you (GridFieldConfig_Base).
The class GridFieldConfig_Base is just a normal GridFieldConfig with some components already added.
One of those components that is already added for you is called the GridFieldSortableHeader which allows you to press on fields to sort the table (which is what produces those buttons that you see).
The reason the Links of the Buttons are dead is probably because there is some routing problem (The GridField is not that well tested in FrontEnd yet) or you maybe have forgotten to add the action AllPages to $allowed_actions.
Solutions:
Plain table
if you don't really need any feature of GridField, and you just want a plain table, the easiest way is to just set an empty config:
public function AllPages() {
$config = GridFieldConfig::create();
$dataColumns = GridFieldDataColumns::create();
$dataColumns->setDisplayFields(array(
'Title' => 'Title',
'URLSegment'=> 'URL',
'LastEdited' => 'Changed'
));
$config->addComponent($dataColumns);
$gridField = GridField::create('pages', 'All pages', SiteTree::get(), $config);
return Form::create($this, __FUNCTION__, FieldList::create($gridField), FieldList::create());
}
Remove just the Sortable Header
$gridField->getConfig()->removeComponentsByType('GridFieldSortableHeader');
// if you don't have a SortableHeader, you probably also don't want a filter
$gridField->getConfig()->removeComponentsByType('GridFieldFilterHeader');
Replace the Sortable Header with a text only header row
Unfortunately, there is no normal header in SilverStripe at this time, but the great gridfieldextensions module from Andrew Short brings you one.
Get the module on GitHub or Packagist
$gridField->getConfig()->removeComponentsByType('GridFieldSortableHeader');
// if you don't have a SortableHeader, you probably also don't want a filter
$gridField->getConfig()->removeComponentsByType('GridFieldFilterHeader');
$gridField->getConfig()->addComponent(new GridFieldTitleHeader());
fix the Sortable Header
if you wish to have the sort functionality, you will have to fix the routing.
It has been a while since I last used the GridField in frontend. I can only tell you that it did work at some time.
perhaps routing does not work because your form action (AllPages) is not accessible as URL, if that's the case, its pretty easy to fix: just add AllPages to your $allowed_actions of your Controller.
if the form is accessible, then its probably a bug in GridField, I would need to debug that to tell you any more. If that is the case, please reply via comment or contact me on IRC and I will take a look at it.
UPDATE: I just answered another frontend GridField question, and went a bit more in depth. perhaps this is also helpful to you: https://stackoverflow.com/a/22433159/1119263 (see Option 2)
Related
I have one view in Drupal 7, which displays user information like (Name, address, Status, etc..). I have one column in this (Table)view as "Published event". Basically events are created by users do I want to make this column sortable. I have attached image for more reference.
I tried with applying relationship but no success.
table settings
my handler code is like below :
$handler->display->display_options['sorts']['event_count_published'] ['id'] = 'event_count_published';
$handler->display->display_options['sorts']['event_count_published'] ['table'] = 'search_api_index_user_search_index';
$handler->display->display_options['sorts']['event_count_published'] ['field'] = 'event_count_published';
$handler->display->display_options['sorts']['event_count_published'] ['order'] = 'DESC';
'mail' => array(
'sortable' => 1,
'default_sort_order' => 'asc',
'align' => '',
'separator' => '',
'empty_column' => 0,
),
'event_count_published' => array(
'align' => '',
'separator' => '',
'empty_column' => 0,
'sortable' => 1,
),
above code is in "tcd_reporting.views_default.inc" file, if I put 'sortable => 1', it still does not provide sorting
field is created by below code:
$properties['event_count_published'] = array(
'label' => t('Published Events'),
'description' => t('Number of published events authored by user.'),
'type' => 'integer',
'getter callback' => 'tcd_event_content_type_count_published_get',
'computed' => TRUE,
'entity views field' => TRUE,
);
[Introduction] Which function is responsible for 'click sort' in views?
Click sort -this checkbox from your second screen- in views table settings is function which is enabled only for fields which have properly defined handlers. As you may know each field in views have few handlers (for displaying, filtering, sorting). And for click sort to be possible on specified column its field handler must have two functions defined: click_sortable and click_sort. First one just need to return true, while second need to properly implements sorting on view. For example see handler: [views_module_path]/handlers/views_handler_field.inc.
Your case:
It seems that your column "Published event" have defined handler which does not have click_sortable and click_sort functions (or click_sortable simply returns false).
Possible fix:
Find place where you defined your view source (it depends on how you informed views about it, if I understand its something like "User info" - maybe in hook_entity_info function or hook_views_data function), check what handler is assigned to your "Published event" field and change it.
It's hard to tell where you need to look as it depends on your implementation.
I suggest you to try create hook_views_data_alter function and dpm() it for start. Later you can alter it like that:
mymodule_views_data_alter(&$data) {
$data['some_view_info']['published_event']['field']['handler'] = 'views_handler_field_numeric';
}
Edit 1
First could you tell where this code is? Is it inside handler class, or maybe some views hook? Views gives you a lot of flexibility but this make them hard to understand, and I'm not sure what exactly you achieve and how.
Assuming your field works properly you can try to simply enable click sort.
Example: I created hook_views_data_alter function to see content of views data
function mymodule_views_data_alter(&$data) {
dpm($data,'d');
}
You might need to clear cache to see dpm of *_alter hooks.
Inside dpm'ed array I found "users" for generic example, and its field name looks like this:
I suggest you to try alter your field with click_sortable = TRUE and see what happens. If this wont help please provide more information about your field, how you created it, how it looks in hook_views_data_alter and which handlers it has defined.
Edit 2
Ok, so you have your views exported to code into views_default file. But this only allows you to export view you created from database to code, so it is basically a reflection of what you done in views web editor (eg. page yourwebsite.com/admin/structure/views/view/your_view_name/edit). What you need to do is to change behavior of one of your fields so it became sortable (add click_sortable and click_sort functions in handler class) or change handler of this field to one with sorting option (change field handler to other one like views_handler_field_numeric). If you don't have experience in creating handlers and this is one of generic handlers i suggest you to go back to my Edit 1, examine your dpm, and try to alter $data array to find solution.
Edit 3
Little explanation to prevent confusion. When creating new view you select collection on which this particular view base on (simpliest example - it may be MySQL table, and view will use SQL queries to retrieve data from it). By digging down we have:
Collection - eg. User which is database table user, it is what you select as source when creating new view.
Field - eg. mail which is database column mail, this fields you add to your view.
Field handler - eg. views_handler_field_numeric, this is class name of handler to use by specified field
Now, if you don't created your own handler then your field "Published event" have one of generic views handler. You shouldn't ever change code of contributed modules - especially so widely used as views handlers. That's why my suggestion to add functions click_sortable and click_sort is incorrect. Instead you should change handler responsible for field "Published event".
Best way is to define proper handler in place where you define your field "Published event". If it's somehow impossible the only way I can think of is hook_views_data_alter see docs for more info and examples. I suppose you should try to redefine handler of your field to generic numeric handler views_handler_field_numeric as it should have full sorting functionallity, or try to add click_sortable property to field array as you can see in first image of my post, but I can't provide you fully tested example.
I'm adding a custom drop down to the userform module and everything seemed to work, however:
If I set the following:
[Textfield One] - Dropdown option one
[Textfield Two] - Dropdown option two
It seems to only save the the setting under Textfield One, and ignores Textfield Two. If I then go back and set Textfield Two again, it wipes out Textfield One.
Am I supposed to iterate over something save each value independently or ...?
The class;
class CustomEditableFormField extends DataExtension
{
public function updateFieldConfiguration(FieldList $fields)
{
$fields->push(DropdownField::create($this->owner->getSettingName('CustomTextId'), 'Custom field')
->setSource(array("1" => "One", "2" => "Two"))
->setEmptyString($this->owner->getSetting('CustomTextId')));
}
}
and my config.yml
EditableFormField:
extensions:
- CustomEditableFormField
The correct way to add a custom extension is:
public function updateFieldConfiguration(FieldList $fields)
{
$fields->push(DropdownField::create(
$this->owner->getSettingName('CustomTextId'),
'Custom field',
$this->getData(),
$this->owner->getSetting('CustomTextId'))
);
}
The main issue being in where the getSetting function is called.
I think you want to subclass EditableFormField, not decorate it.
The general rule of thumb is one field to one piece of data too (but not necessarily).
And that form fields are generic, not specific to a purpose, as that's why they're editable rather than an unending array of use-cases.
So I'm not sure this solution is what you're after.
Especially since EditableFormField::getFieldConfiguration() does not take any parameters, let alone an entire FieldList.
It seems like you're trying to just skip the configuration step of adding a form field, in which case a custom subclass is surely your answer.
I have a few general questions about modifying Magento's admin section and would be grateful to have them answered. I'm new to Magento so please bear with me.
My goal is to add a new column with a product attribute (e.g. "Size") to the "Category Products" table within the Catalog -> Manage Cateories section (see screenshot below).
Having little Magento dev experience, I'm not quite sure where to start. I had a look in some of the Grid.php files under the adminhtml directory, and while I see a bunch of statements like addColumn(...), I'm not sure where I'd slot in my new attribute column.
Also, I assume that instead of modifying any core files directly, I'd copy them to the same path under the local folder and edit or somehow extend them there? Do I have to edit any config files or do anything else for the change to be reflected? Am I - by doing this - in effect creating my own module?
I also read that I should disable "Compilation" before I make any changes. Why is this? Is there anything else to consider?
Again I am very grateful for any help and appreciate that my questions must seem basic. Any supplementary resources you could point me towards would be appreciated. Thanks.
Indeed you should start by understanding what file to edit and how to edit it. In this case you want to modify app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php but, like you said, you should not modify the file in its current location. There are two ways to modify the file in the "correct" way.
(harder but more extensible) Create a new Module in local and tell Magento in the etc/config.xml that you are overwriting that Block (which is just a php class) with a different block in this new Module and have the new class extend the core Block class. Then you just need to overwrite one function (_prepareColumns).
(easier) Copy the file from app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php to app/code/local/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php and modify the function you want (_prepareColumns)
If you are new to Magento, I recommend going with the second option because its easier. Magento will always load the file from local before it loads from core so the file in core will no longer be used and your version in local will be used. To find out more, read this article from Alan Storm
Now in order to add the column you want, do something similar to the SKU field
$this->addColumn('size', array(
'header' => Mage::helper('catalog')->__('Size'),
'index' => 'size'
));
in the order you want it (between Product Name and SKU). I am assuming that your Products have a field called size that you can retreive with $product->getSize()
Max solution was pretty spot on but missing some important steps, I'll elaborate on his original method
Create a new local override of the Product Tab by copying app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php to app/code/local/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php
There are 2 functions involved in modifying the grid view. _prepareCollection and _prepareColumns
_prepareColumns by adding a call to the addColumn function just like:
$this->addColumn('size', array(
'header' => Mage::helper('catalog')->__('Size'),
'width' => '80',
'index' => 'size'
));
_prepareCollection, by default the product collection loaded in the grid only has a few attributes(name,sku,price) what you need to do add our now attribute by ->addAttributeToSelect('size') now if you are only working with a textfield attribute then this is the extend of the modifications you have to do however if your attribute is for example a dropdown you will need to do further changes to the prepare collection:
(optional) dropdown attributes only store the value of the option that was select so we need to provide an options array to the addColumns call so Magento can display the values correctly, we can do that in the following maner:
on your local copy of Products, add the following to the _prepareColumns functions
$attribute = Mage::getModel('eav/config')->getAttribute('catalog_product', 'colour');
$options = array();
foreach( $attribute->getSource()->getAllOptions(true, true) as $option ) {
$options[$option['value']] = $option['label'];
}
$this->addColumn('colour', array(
'header' => Mage::helper('catalog')->__('Colour'),
'width' => '80',
'index' => 'colour',
'type' => 'options',
'options' => $options
));
While those are some very thorough question and I'm sure you will learn a lot, there is a ready-made solution; Enhanced Admin Product Grid has the ability to add arbitrary attributes as columns.
At the moment I am trying to force "current language" onto the list of the options passed into node_search_execute. Unfortunately I'm having trouble finding the right place to put the function hooks. Perhaps I am missing something simple.
I've got myself down to two basic possibilities for how this should be implemented.
(1) Implement hook_search_info and hook_search_execute
In this case, I'd copy the code from node_search_execute and add a line to it that adds "AND Language = '$current_language'" to the search query.
In my theme folder I've tried adding the functions mythemename_search_info and mythemename_search_execute - but they do not execute. When run.
function mythemename_search_info() {
return array(
'title' => 'Content',
'path' => 'node',
'conditions_callback' => 'mythemename_search_execute',
);
}
function mythemename_search_execute($keys = NULL, $conditions = NULL){
return array();
}
In this example - I'd just hope to get "no results" so I could be sure the override was running, then I'd implement the full search functionality.
(2) Implement hook_search_preprocess()
I also tried mythemename_search_preprocess()
function mythemename_search_preprocess($text) {
// Do processing on $text
echo $text; die();
$text = "french";
return $text;
}
But again, I don't get the expected results (a white page with a bit of text on it)
So whatever I'm doing, these search hooks are not getting detected.
What's missing? Do they perhaps have to be in a module?
Yes they do need to be in a module, most hooks are only called for modules and not themes. The most notable exception to this would be theme/preprocess hooks which are called for both.
In case you haven't made one before it's pretty straightforward to create a custom module, there's an invaluable guide here.
I used hook_search_info(), hook_search_execute() and hook_search_access() in my custom module. replaced "hook" with module name. I was able to get the tab created with 'title' of hook_search_info().
and passed he results array in hook_search_execute. with this the results started showing under the tab in search page. So definitely creating a new module will be of help to get a new search tab included.
I have a module that builds a form that includes a fieldset. Instead of using the <legend> element to render the fieldset title, I want to place this content in a <div> element instead. But I want to change the behavior only for the form returned by my module, so I don't want to place any new functionality into my theme's template.php file.
In mymod.module I have defined:
// custom rendering function for fieldset elements
function theme_mymod_fieldset($element) {
return 'test';
}
// implement hook_theme
function mymod_theme() {
return array(
'mymod_fieldset' => array('arguments' => array('element' => NULL)),
'mymod_form' => array('arguments' => array())
);
}
// return a form that is based on the 'Basic Account Info' category of the user profile
function mymod_form() {
// load the user's profile
global $user;
$account = user_load($user->uid);
// load the profile form, and then edit it
$form_state = array();
$form = drupal_retrieve_form('user_profile_form', $form_state, $account, 'Basic Account Info');
// set the custom #theme function for this fieldset
$form['Basic Account Info']['#theme'] = 'mymod_fieldset';
// more form manipulations
// ...
return $form;
}
When my page gets rendered, I expected to see the fieldset representing 'Basic Account Info' to be wholly replaced by my test message 'test'. Instead what happens is that the <fieldset> and <legend> elements are rendered as normal, but with the body of the fieldset replaced by the test message instead, like this:
<fieldset>
<legend>Basic Account Info</legend>
test
</fieldset>
Why doesn't my #theme function have the chance to replace the entire <fieldset> element? If I wrap a textfield in this function instead, I am able to completely replace the <input> element along with its label. Furthermore, if I provide an override in my site's template.php for theme_fieldset, it works as expected and I am able to completely replace the <fieldset>, so I know it is possible.
What's different about providing #theme functions to fieldsets inside a module?
Have you tried overriding theme_fieldset() instead of using the #theme function? I believe you could do something like this in your .module file:
function mymodule_fieldset($element) {
// do something;
return $html;
}
This would apply to all fieldsets. You could do some kind of check on $element for the fieldsets you want to affect and then use the default implementation for all others.
Take a look at: http://api.drupal.org/api/function/theme_fieldset/6
I know this is an old post -- but I've run into the same issue. I came up with an ugly work around. This is definitely a bug in the Form API. Maybe my temporary fix will be helpful to someone.
I found (and appended) a bug report here: http://drupal.org/node/225698
Worth checking that before trying my hacky fix.
I'm not sure what the children are in $form['Basic Account Info'] in this example, but basically what you can do is use drupal_render() on that fieldset's children, and then recreate a fieldset array separate from $form['Basic Account Info'], theme it with theme() and pass it back to the form array as markup..
$fieldsetElement = array(
//$child is the key of whatever child you need in the fieldset
//you may have to alter this for multiple children, stringing
//together multiple drupal_render calls on each children
//(#children needs to be a string.. unless your theme can handle the array)
'#children'=>drupal_render($form['Basic Account Info'][$child]),
'#attributes'=>array(),//set real attributes
'#title'=>$form['Basic Account Info']['#title']
);
$form['Basic Account Info'] = array(
'#type'=>'markup',//not really needed, is default
'#value'=>theme('mymod_fieldset',$fieldsetElement)
);
super-duper hacking, likely causes disconnect with form state and potential validation failure -- but both are fixable by trial and error with the form api. I wouldn't recommend this unless you really want to get your hands dirty with PHP and drupal form API, but that's really the only way, unless you can live without variable fieldset themes in your module... Maybe try prefix and suffix?
This is just off the top of my head but maybe the difference is because a fieldset is not a form element but just a seperator or a grouper, if you will. Maybe the #theme callback is only for form elements?
The concept of your code works, meaning you can do what you want to do.
There are some things that can explain why it doesn't work.
The fieldset is not $form['Basic Account Info'].
Need to clear cache.
$form['Basic Account Info']['#theme'] is lost/overridden later in the code execution.
Try to take a look at $form before you do any of the moderations. When I tried to copy your code I run into a bug:
user.pages.inc file needed to be loaded
I was having the same issue.
You need to use #theme_wrappers instead of #theme
'#type' => 'fieldset',
'#theme_wrappers' => array('mymodule_fieldset'),