I would like to change the the labels of pages in Yii.
I used Zii.widegt.CListView to show the list of items. The default structure of yii pagination is [previous] 1 2 4 5 6 7 [next] required structure is < 1....10 11 12 13 14 ....40 >.
I read "How can I customize the labels for the pager in Yii?" which is helpful, but how can I show the firstPageLabel as page number 1 instead of << and lastPageLabel as 40 instead of >>.
If you can't find a way to pass in the total item count (i.e. 40) to the lastPageLabel override, you will need to override the CLinkPager class to have this work automatically. The $lastPageLabel is static in the current implementation and does not provide access to variables like "itemCount". You can see the code:
$buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,self::CSS_LAST_PAGE,$currentPage>=$pageCount-1,false);
It just echos $this->lastPageLabel, which is static text.
If you make a new pager (called, say, MyLinkPager), use it like so:
$this->widget('zii.widgets.CListView', array(
'dataProvider' => $categoryProjects,
'itemView' => '_itemDetailsView',
'ajaxUpdate' => false,
'pager' => array(
'class' => 'MyLinkPager', // here is your pager
'firstPageLabel' => '<<',
'prevPageLabel' => '<',
'nextPageLabel' => '>',
'lastPageLabel' => '>>',
),
));
You will have to create your own class that derives from CLinkPager. Ultimately, what you want to achieve is to change the line that thaddeusmt mentions, inside CLinkPager::createPageButtons:
$buttons[]=$this->createPageButton($this->lastPageLabel /* the rest doesn't matter */);
to do the equivalent of
$buttons[]=$this->createPageButton($pageCount /* the rest doesn't matter */);
Now obviously the direct way of doing this is by overriding createPageButtons, but that's not a trivial method and if you do override it completely, you risk your pager becoming "out of sync" with code on later versions of Yii. So let's look for alternatives.
Alternatives
(you might want to skip this part if you 're only interested in the solution)
One alternative would be to override the method, have it call the standard implementation and then simply change what you need to change:
protected function createPageButtons() {
$buttons = parent::createPageButtons(); // Yii's implementation
array_pop($buttons); // remove last item, which is the link for the last page
$buttons[]=$this->createPageButton($this->getPageCount() /* the rest unchanged */);
return $buttons;
}
That's better, but it still involves copy/pasting code so your implementation needs to keep that part in sync with future Yii releases. Can we do better than that? It turns out that yes. Here's the method CLinkPager::run:
public function run()
{
$this->registerClientScript();
$buttons=$this->createPageButtons();
if(empty($buttons))
return;
echo $this->header;
echo CHtml::tag('ul',$this->htmlOptions,implode("\n",$buttons));
echo $this->footer;
}
As you see, CLinkPager doesn't really do a lot other than call createPageButtons. So you could override run and dynamically set the value of $this->lastPageLabel before letting Yii's code run, like this:
public function run()
{
$this->lastPageLabel = $this->getPageCount();
parent::run();
}
Well, this is nice. We managed to achieve the goal by overriding just one method and writing two lines of code. As an added bonus, there's nothing in our code that needs to be kept in sync with Yii if the implementation of CLinkPager changes in the future.
On the other hand, all of these solutions introduce an impurity that could be problematic: when someone writes a view that uses our custom pager class, they might not know that we are actually overriding the value of lastPageLabel! Imagine the "why is it not outputting the label I 'm telling it to?" confusion.
A really nice solution
Fortunately, you can have your pie and eat it too by overriding CLinkPager::init like this:
public function init()
{
// "Hijack" the default values for properties that the user did not set.
// This allows the user to still override this if they want to.
if($this->nextPageLabel===null)
$this->nextPageLabel='<';
if($this->prevPageLabel===null)
$this->prevPageLabel='>';
if($this->firstPageLabel===null)
$this->firstPageLabel='1';
if($this->lastPageLabel===null)
$this->lastPageLabel=$this->getPageCount();
// and let Yii do the rest like it always does
parent::init();
}
You can then configure your view to use this pager, and everything will work just fine without any further ado:
'pager' => array('class' => 'CustomLinkPager'),
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've got the following situation. As in your typical ZF2-Application there is existing a layout.phtml-view-script for the layout and a view-script specific to the called action. In my case it's team/frontend/index.phtml.
My problem is concerned with the headtitle. The headtitle-parts are currently set directly within the view-script-files, like the following:
layout.phtml
<?php
echo $this->headtitle('My Application')->setAutoEscape(false)->setSeparator(' | ');
team/frontend/index.phtml
<?php
$this->headtitle('Our team');
This is - as intended - resulting in My Application | Our team. So far so good.
Now I am writing a new module, which - beside some other features - also should provide the possibility for some SEO-stuff. One of the SEO-tasks is to overwrite the title of a the inner view script: team/frontend/index.phtml but not for layout.phtml. The new title is coming from the database, however this is not relevant for this problem.
So for this action I want to be able to produce an outcome like this: My Application | The faces behind the team. As you can see I only want to overwrite the everything the action-view-script sets, but not the title part of the layout.phtml.
Since it's a completly different module, which would add this functionality and both modules should work independendly I hope this is solvable through events/the EventManager.
Sidenote: the new module is called: Node
I tried 2 things, both resulting in the same thing:
I attached to the MvcEvent::EVENT_RENDER EventManager within the onBootstrap-method of my Node\Module.php and (in another attempt) I fetched EventManager of Zend\View\View and attached to the ViewEvent::EVENT_RENDERER_POST.
In the callback-function I fetched the title from the database and set it by fetching the HeadTitle-View-Helper.
Both attempts resulted in a final headtitle of My Application | Our team | The faces behind the team meaning the parts were just appended although I used the SET-Parameter within the callback function.
This is a simplified version of the callback-function:
$viewHelperManager = $serviceLocator->get('viewhelpermanager');
// Get new title-part from database
$titlePart = '...' // In this case "The faces behind the team"
// Set HeadTitle
$headtitle = $viewHelperManager->get('headtitle');
$headtitle($node->getNodeName(), 'SET');
As you can see here I am using SET as the second parameter. I do understand, why it's not working: it's too late the event seems to be triggered when the action-view-script and the layout-view-script are finished processing. However I need a possibility to hook in before the layout-view-script is processed, so that I can overwrite the action-view-scripts' headtitle.
I hope you understand what I mean.
Any thoughts ideas on this? Is there an event which is triggered for every view-script in the queue?
UPDATE 2015-10-14 - 13:10
I've further investigated the code and the event triggering within the code of ZF2. Because of the structure in which it is written, my request is just not possible in the way I wanted it to be.
Like Wilt's and akond's anwers the headtitle should be generally dealt with in the action or in particular other places, however not within the view-script itself.
Wilt posted the link to the ZF2-docs concerning headtitle. There they provide an example of how to set the headtitle within an action.
I knew how to do that, however in the Album-Module Tutorial (http://framework.zend.com/manual/current/en/user-guide/database-and-models.html#listing-albums) they set the headtitle within the view, so I went this way...
Of course, it's no problem for me to fix my other modules (like my Team-Module), however I will run into problems with vendor-modules. If authors of other module keep setting their headtitles within their view-scripts, my Node-Module won't stand a chance. The only thing I could do in that case is to overwrite their view-scripts and remove the setting of the headtitle...
In my opinion you are experiencing this problem because of contamination of the view with data. In other words, you should not have put neither 'My Application' or 'Our team' into the view.
What you should be having instead is a model/helper, that provides view with an appropriate head title. Something along the lines of:
$this->view ()->headtitle ('Our team');
in the controller action and
echo $this->headtitle
in the view.
View should only render data that is provided by model. In our case, the view is a model in its own right. That's bogus.
You should set your head title in your controller not in your view.
Check the ZF2 documentation on head title to see how to properly use this view helper:
$headTitle = $viewHelperManager->get('headTitle');
$headTitle->setSeparator(' | ');
$headTitle->append('My Application');
$headTitle->append('Our team');
In the view only:
<?php echo $this->headTitle() ?>
Outputs the following html:
<title>My Application | Our team</title>
In your other module, in the other controller you can set new variables for the headscript.
Another possibility (don't think it is better) would be to pass your titles as variables to your view in a parameter. For example like this:
$view = new ViewModel(array(
'titles' => array(
'layout' => 'My Application',
'action' => 'Our team'
)
));
$view->setTemplate('template');
return $view;
Now in the views you can do:
$this->headtitle($this->titles['layout'])->setAutoEscape(false)->setSeparator(' | ');
and
$this->headtitle($this->titles['action']);
This last solution is more dirty, because you are not supposed to set your head title like that in the view if you ask me. But it will work and it suits your current solution more (less refactoring).
UPDATE
If you are afraid of others overruling the title you set in their views then you can also extend the the HeadTitle view helper and overwrite the '_invoke' method so it does not allow the overwriting the $title if it is already set.
And then re-register (overwrite) it in the view helper manager:
'view_helpers' => array(
'invokables' => array(
'headTitle' => 'My\Custom\View\Helper\HeadTitle',
)
)
CakePHP v.2.5.x
I want to modify the defaults for a form to add my own css classes to the wrapping div. I know how to do this, but I'm wondering if there is a way to do it without overwriting CakePHP's defaults. For example, with no $options['class'] the system adds input-specific classes dynamically, like .error etc. In other words, I want the defaults AND my additions, not just my additions.
echo $this->Form->create('Listing');
$this->Form->inputDefaults(array(
'div' => array(
'class' => 'default-class'
)
)
);
//will result in .default-class because of default above
echo $this->Form->input('title', array(
)
);
//will result in .adhoc-class because we just overrode the default
echo $this->Form->input('address',
array(
'div'=>array('class'=>'adhoc-class'),
)
);
In the above examples, I'd like the first to have the default class that I set, PLUS CakePHPs dynamic classes. In the second, I'd like the .adhoc-class to be added to those same classes. Is this possible? In case this isn't already clear, it's kind of a two-part question: how to make the default class settings "additive" and secondly how to make the individual input settings additive.
from the API : http://api.cakephp.org/2.5/class-FormHelper.html#_input
according with http://api.cakephp.org/2.5/source-class-HtmlHelper.html#951-970
it seems not possible for add a class at your "default class"
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'),