Add a button on the login form (D8) - hook

Is it possible to add a button on the basic login form?
function alter_form_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id)
{
if ($form_id == 'user_login_form') {
##ADD BUTTONS ???
$form['#validate'] = ['test_validate'];
$form['actions']['submit']['#submit'][] = 'custom_submit_method';
}
}

You can add a button in the actions wrapper using the following code
$form['actions']['custom_submit'] = [
'#type' => 'submit',
'#name' => 'custom_submit',
'#value' => t('My custom submit button'),
];
If you want to distinct both click, for example if you need to do something else when custom_submit is used, then you will need to access the $form_state->getTriggeringElement(); in your custom_submit_method submission handler.
function custom_submit_method(array $form, FormStateInterface $form_state){
$trigger = $form_state->getTriggeringElement();
if ($trigger['#name'] === 'custom_submit') {
// ...
}
}
You may find more documentation about
Usage of ::getTriggeringElement: https://drupal.stackexchange.com/questions/223289/how-to-get-triggering-element
Usage of custom submisson handler:
https://drupal.stackexchange.com/questions/223342/add-a-custom-submission-handler-to-a-form
I hope my answer will help you.

Related

Using a custom filter button to filter a list, works on first click but subsequent clicks don't work as well! SPFX

I have 2 SP lists (A and B).
List A has filter buttons next to each list item. When a user clicks a button it should filter List B, only showing the related items.
List A has an Id column which List B matches it's column (MasterItems) with List A's Id.
Here's the code I'm using:
public _getListItems() {
sp.web.lists.getByTitle("ListA").items.get().then((items: any[]) => {
let returnedItems: IListAItem[] = items.map((item) => { return new ListAItem(item); });
this.setState({
Items: returnedItems,
ListAItems: returnedItems,
});
});
sp.web.lists.getByTitle("ListB").items.get().then((items: any[]) => {
let returnedItems: IListBItem[] = items.map((item) => { return new ListBItem(item); });
this.setState({
ListBItems: returnedItems, //This brings in the items from ListB so they can be filtered on this.state.ListB when clicked
});
});
}
private _editItem = (ev: React.MouseEvent<HTMLElement>) => {
this._getListItems(); //This attempts to reset the list when another filter is clicked, but is half working!
const sid = Number(ev.currentTarget.id);
const sid2 = 'DIBR'+sid;
let _item = this.state.ListBItems.filter((item) => { return item.MasterItem == sid2; });
if (_item && _item.length > 0) {
sp.web.lists.getByTitle("ListB").items.get().then((items: any[]) => {
let returnedItems: IListBItem[] =
items.filter(i => _item.some(other => other.Id === i.Id)).map(
(item) => new ListBItem(item)
);
this.setState({
ListBItems: returnedItems,
});
});
}
}
The problem is that when the button is clicked next to an item, it filters correctly on first click!
but if filtered again on the same or different item it will sometimes unset the filter and mix results, other times it will filter correctly. So I'm suspecting I've made a state problem here, but can't seem to discover why.
Regards,
T
UPDATE: I've added a clear filter button which makes things work, but would like the user to be able to click on filter to filter instead of having to clear it each time.
I am doing the same in my SharePoint list
so basically I always set the clear filter function before the filter function,
for example:
function myFilter(){
//my filter code goes here
}
function clearFilter(){
//the clear filter code goes here
}
lets say you are running the function on an item select or a button click or text input change, set the clear filter to run before the filter.
function funcGroup{
clearFilter();
setTimeout(() => {
myFilter();
}, 300);
}
or
function funcGroup{
setTimeout(() => {
clearFilter();
}, 300);
myFilter();
}
I am using this scenario with my SharePoint lists and its working perfect...

How to implement a pagination for a search module in Zend Framework 2?

I have a module Search in my ZF2 application. The user fills in a search form out and gets a list of courses.
Now I'm adding the pagination to the module. The paginator is basically working: I can retrieve data over it and the pagination is displayed correctly (pagelinks 1-7 for 70 found courses with the dafault setting 10 items per page).
But it's still not usable. When I click on a pagelink, the form POST data is lost. I know -- it cannot work the way, how I implemented it (see the code below). But I have no idea, how to do it correctly, in order to eep checking the form data and nonetheless be able to use pagination.
That is my code:
Table class Search\Model\CourseTable
class CourseTable {
...
// without pagination
// public function findAllByCriteria(CourseSearchInput $input) {
// with pagination
public function findAllByCriteria(CourseSearchInput $input, $pageNumber) {
...
$select = new Select();
$where = new Where();
$having = new Having();
...
// without pagination
// $resultSet = $this->tableGateway->selectWith($select);
// return $resultSet;
// with pagination
$adapter = new \MyNamespqce\Paginator\Adapter\DbSelect($select, $this->tableGateway->getAdapter());
$paginator = new \Zend\Paginator\Paginator($adapter);
$paginator->setCurrentPageNumber($pageNumber);
return $paginator;
}
...
}
Search\Controller\SearchController
class SearchController extends AbstractActionController {
public function searchCoursesAction() {
$form = $this->getServiceLocator()->get('Search\Form\CourseSearchForm');
$request = $this->getRequest();
if ($request->isPost()) {
$courseSearchInput = new CourseSearchInput();
$form->setInputFilter($courseSearchInput->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$courseSearchInput->exchangeArray($form->getData());
// without pagination
// $courses = $this->getCourseTable()->findAllByCriteria($courseSearchInput);
// with pagination
$page = $this->params()->fromRoute('page');
$paginator = $this->getCourseTable()->findAllByCriteria($courseSearchInput, $page);
} else {
$paginator = null;
}
} else {
$paginator = null;
}
return new ViewModel(array(
'form' => $form,
// without pagination
// 'courses' => $courses,
// with pagination
'paginator' => $paginator,
'cities' => ...
));
}
...
}
How to get it working?
I also have the same problem, and I have solved it. But this is not good way. May be the idea will help you.
I solved it as follow: (Search pagination for Zend tutorial album module)
I build two action in controller named "search" and "index".
Whenever the search form submitted, it always post the value to search action. Search action build the url with search parameters, and redirect to index to disply search result.
And when the pagination links clicked, then posted values are passed through url. So whenever index action ask for search parameters, it always get the values in same format.
I defined route as follows:
'album' => array(
'type' => 'segment',
'options' => array(
'route' => '/album[/:action][/:id][/page/:page][/order_by/:order_by][/:order][/search_by/:search_by]',
'constraints' => array(
'action' => '(?!\bpage\b)(?!\border_by\b)(?!\bsearch_by\b)[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
'page' => '[0-9]+',
'order_by' => '[a-zA-Z][a-zA-Z0-9_-]*',
'order' => 'ASC|DESC',
),
'defaults' => array(
'controller' => 'Album\Controller\Album',
'action' => 'index',
),
),
),
There is a parameter named "search_by", which will keep all search parameters as a json string. This is the point, which is not good I know, but have not find any other way yet.
"Search" action build this string as -
public function searchAction()
{
$request = $this->getRequest();
$url = 'index';
if ($request->isPost()) {
$formdata = (array) $request->getPost();
$search_data = array();
foreach ($formdata as $key => $value) {
if ($key != 'submit') {
if (!empty($value)) {
$search_data[$key] = $value;
}
}
}
if (!empty($search_data)) {
$search_by = json_encode($search_data);
$url .= '/search_by/' . $search_by;
}
}
$this->redirect()->toUrl($url);
}
And next index action decode the string, do necessary action, and also send the json string to view.
public function indexAction() {
$searchform = new AlbumSearchForm();
$searchform->get('submit')->setValue('Search');
$select = new Select();
$order_by = $this->params()->fromRoute('order_by') ?
$this->params()->fromRoute('order_by') : 'id';
$order = $this->params()->fromRoute('order') ?
$this->params()->fromRoute('order') : Select::ORDER_ASCENDING;
$page = $this->params()->fromRoute('page') ? (int) $this->params()->fromRoute('page') : 1;
$select->order($order_by . ' ' . $order);
$search_by = $this->params()->fromRoute('search_by') ?
$this->params()->fromRoute('search_by') : '';
$where = new \Zend\Db\Sql\Where();
$formdata = array();
if (!empty($search_by)) {
$formdata = (array) json_decode($search_by);
if (!empty($formdata['artist'])) {
$where->addPredicate(
new \Zend\Db\Sql\Predicate\Like('artist', '%' . $formdata['artist'] . '%')
);
}
if (!empty($formdata['title'])) {
$where->addPredicate(
new \Zend\Db\Sql\Predicate\Like('title', '%' . $formdata['title'] . '%')
);
}
}
if (!empty($where)) {
$select->where($where);
}
$album = $this->getAlbumTable()->fetchAll($select);
$totalRecord = $album->count();
$itemsPerPage = 2;
$album->current();
$paginator = new Paginator(new paginatorIterator($album));
$paginator->setCurrentPageNumber($page)
->setItemCountPerPage($itemsPerPage)
->setPageRange(7);
$searchform->setData($formdata);
return new ViewModel(array(
'search_by' => $search_by,
'order_by' => $order_by,
'order' => $order,
'page' => $page,
'paginator' => $paginator,
'pageAction' => 'album',
'form' => $searchform,
'totalRecord' => $totalRecord
));
}
All the sorting and paging url contain that string.
If you know all the searching paarameters before, then you can define that at route, and pass like the same way without json string. As I have to build a common search, I have build a single string.
Source code for "Album search" is available in git hub at https://github.com/tahmina8765/zf2_search_with_pagination_example.
Live Demo: http://zf2pagination.lifencolor.com/public/album
#Sam & #automatix in the question comments are both right. My suggestion (though I'm looking for a simpler alternative) is to construct a segment route, which covers all of the options that you're likely to need and start with a standard form POST request.
Then, after the request is validated, pass the form data to the paginationControl helper as follows:
$resultsView = new ViewModel(array(
'paginator' => $paginator,
'routeParams' => array_filter($form->getData())
));
Then, in your view template, set the route parameters in the paginationControl view helper:
<?php echo $this->paginationControl($paginator, 'Sliding', 'paginator/default',
array('routeParams' => $routeParams)
) ?>
I've used array_filter here because it's a really simple way of removing any element from the form data that's null, empty or so on. That way you don't pass in extra data that you don't need.

what's these lines meaning in hook_menu?

Defining page arguments is useful because you can call the same callback from different menu items and provide some hidden context for the callback through the page arguments.
i don't follow this well, expect someone can make an example to me. thank you.
This a very quick exemple. This create a new menu entry, which accept two argument. As for the exemple, I choose $year and $month here. So I'm able to pass a $year and a $month to a page, wich a used in a custom form to do some stuff.
So, you are here able to set a context (a year/month) for a form in a custom page.
/**
* Implementation of hook_menu().
*/
function exemple_menu() {
$items = array();
$items['mydate/%/%'] = array(
'title' => 'Exemple', // NOTE: t() not needed
'page callback' => 'mydate_page',
'page arguments' => array(1, 2),
'access callback' => TRUE, // no access check
);
$return $items;
}
/**
* Page callback.
*/
function mydate_page($year = null, $month = null) {
if (isset($year) && isset($month)) {
$output = drupal_get_form('myFormContentByDate', $year, $month);
}
else {
drupal_set_message('You need to select a date', 'warning');
}
return $output;
}
Hope that helps.

How to add custom blocks in drupal?

I have a simple module which will return a form , but now i have added this to a menu like
admin/settings/
But i want this form in another page ,so i added a hook_block() , my module showed up in the blocks page and i added it to be seen by all in all pages in content area but i dont get that form ? where did i go wrong ? I am new to drupal any help plz
function emp_form_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('New Block');
$blocks[0]['cache'] = BLOCK_NO_CACHE;
return $blocks;
}
}
i am using drupal 6
You should also implement the view op, like this:
case 'view':
return array(
'subject' => t('My awesome form'),
'content' => drupal_get_form('my_awesome_form'),
);
break;

Drupal: Create custom search

I'm trying to create a custom search but getting stuck.
What I want is to have a dropdownbox so the user can choose where to search in.
These options can mean 1 or more content types.
So if he chooses options A, then the search will look in node-type P,Q,R.
But he may not give those results, but only the uid's which will be then themed to gather specific data for that user.
To make it a little bit clearer, Suppose I want to llok for people. The what I'm searching in is 2 content profile types. But ofcourse you dont want to display those as a result, but a nice picture of the user and some data.
I started with creating a form with a textfield and the dropdown box.
Then, in the submit handler, i created the keys and redirected to another pages with those keys as a tail. This page has been defined in the menu hook, just like how search does it.
After that I want to call hook_view to do the actual search by calling node_search, and give back the results.
Unfortunately, it goes wrong. When i click the Search button, it gives me a 404.
But am I on the right track? Is this the way to create a custom search?
Thx for your help.
Here's the code for some clarity:
<?php
// $Id$
/*
* #file
* Searches on Project, Person, Portfolio or Group.
*/
/**
* returns an array of menu items
* #return array of menu items
*/
function vm_search_menu() {
$subjects = _vm_search_get_subjects();
foreach ($subjects as $name => $description) {
$items['zoek/'. $name .'/%menu_tail'] = array(
'page callback' => 'vm_search_view',
'page arguments' => array($name),
'type' => MENU_LOCAL_TASK,
);
}
return $items;
}
/**
* create a block to put the form into.
* #param $op
* #param $delta
* #param $edit
* #return mixed
*/
function vm_search_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('Algemene zoek');
return $blocks;
case 'view':
if (0 == $delta) {
$block['subject'] = t('');
$block['content'] = drupal_get_form('vm_search_general_form');
}
return $block;
}
}
/**
* Define the form.
*/
function vm_search_general_form() {
$subjects = _vm_search_get_subjects();
foreach ($subjects as $key => $subject) {
$options[$key] = $subject['desc'];
}
$form['subjects'] = array(
'#type' => 'select',
'#options' => $options,
'#required' => TRUE,
);
$form['keys'] = array(
'#type' => 'textfield',
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Zoek'),
);
return $form;
}
function vm_search_general_form_submit($form, &$form_state) {
$subjects = _vm_search_get_subjects();
$keys = $form_state['values']['keys']; //the search keys
//the content types to search in
$keys .= ' type:' . implode(',', $subjects[$form_state['values']['subjects']]['types']);
//redirect to the page, where vm_search_view will handle the actual search
$form_state['redirect'] = 'zoek/'. $form_state['values']['subjects'] .'/'. $keys;
}
/**
* Menu callback; presents the search results.
*/
function vm_search_view($type = 'node') {
// Search form submits with POST but redirects to GET. This way we can keep
// the search query URL clean as a whistle:
// search/type/keyword+keyword
if (!isset($_POST['form_id'])) {
if ($type == '') {
// Note: search/node can not be a default tab because it would take on the
// path of its parent (search). It would prevent remembering keywords when
// switching tabs. This is why we drupal_goto to it from the parent instead.
drupal_goto($front_page);
}
$keys = search_get_keys();
// Only perform search if there is non-whitespace search term:
$results = '';
if (trim($keys)) {
// Log the search keys:
watchdog('vm_search', '%keys (#type).', array('%keys' => $keys, '#type' => $type));
// Collect the search results:
$results = node_search('search', $type);
if ($results) {
$results = theme('box', t('Zoek resultaten'), $results);
}
else {
$results = theme('box', t('Je zoek heeft geen resultaten opgeleverd.'));
}
}
}
return $results;
}
/**
* returns array where to look for
* #return array
*/
function _vm_search_get_subjects() {
$subjects['opdracht'] =
array('desc' => t('Opdracht'),
'types' => array('project')
);
$subjects['persoon'] =
array('desc' => t('Persoon'),
'types' => array('types_specialisatie', 'smaak_en_interesses')
);
$subjects['groep'] =
array('desc' => t('Groep'),
'types' => array('Villamedia_groep')
);
$subjects['portfolio'] =
array('desc' => t('Portfolio'),
'types' => array('artikel')
);
return $subjects;
}
To be honest, I haven't seen many people implement hook_search. Most just use Views, or, for advanced things, something like Faceted Search.
Did you consider using either for your current project? Why didn't it work?
you could also use a combination of hook_menu for your results, and db_queries with your custom (and optimized so faster) queries.
For example:
search/%/%
where the arguments could be whatever you need, for example the first one for minimum price, the second price to the maximum price, third for minimal bedrooms... Your url would look always like that:
search/200/400/null/3/ ...
I have used a null, but it could be anything that you prefer to consider this field as empty.
Then, from your select form you have just to redirect following the structure of this url and adding the parameters in its correct place.
It is probalby not the most beautiful way of building a url, but using this technique and hook_theme will allow you to have an unlimited flexibility. I can show you a project where we are using this technique and, I think it looks pretty good :-).
Any comment regarding this would be much aprreciated :-).

Resources