WordPress API passing email argument issue - wordpress-rest-api

Using a WordPress REST API custom endpoint, I am attempting to get user data (or at least the user id) with the following code in the functions.php file:
function getUser(WP_REST_Request $request) {
global $wpdb;
$email = $request->get_param( 'email' );
$query = "SELECT * FROM wp_users WHERE user_email = $email";
$result = $wpdb->get_results($query);
return $result;
}
add_action( 'rest_api_init', function () {
register_rest_route( 'myapi/v1', '/getcustomer/(?P<email>[^/]+)', array(
'methods' => 'GET',
'callback' => 'getUser'
) );
} );
Testing the function with the endpoint /wp-json/myapi/v1/getcustomer/joe#anymail.com it returns with empty brackets [ ]. Am I missing something here? Any help would be greatly appreciated.

There are multiple issues with your code:
You should encode your user emails or send it via POST method.
Your current query is open to SQL Injection
Your value must be enclosed in quotes. Now it translates to .. WHERE user_email = joe#anymail.com and that is SQL syntax error.
So your code should look like this:
$query = "SELECT * FROM wp_users WHERE user_email = %s";
$result = $wpdb->get_results($wpdb->prepare($query, $email));

Related

How can I refine a REST query based on a linked module?

I'm trying to get all accounts that do not have an associated document.
This is what I have in mind:
$parameters = array(
'session' => $this->getSessionId(),
'module_name' => 'Accounts',
'query' => "accounts.id NOT IN (SELECT DISTINCT account_id FROM documents_accounts)",
);
but of course it doesn't work. I get an "Access denied" error (40).
Ideally I'd like to filter even further by querying for accounts that do not have an attached document of a given type. For example:
getAccountsWithout('contracts');
getAccountsWithout('quote1');
...
It could be that your are not correctly authenticating with the REST API. Or that your are not correctly provide what the method call needs.
If that is not the issue. One solution is to create a customer version the service/v4_1. So that you can add your own custom call.
This can be achieved by copying the v4_1 directory giving it a name like v4_1_1.
Rename the following:
SugarWebServiceImplv4_1.php to SugarWebServiceImplv4_1_1.php
SugarWebServiceUtilv4_1.php to SugarWebServiceImplv4_1_1.php
Change the class signatures to this:
SugarWebServiceImplv4_1:
class SugarWebServiceImplv4_1_1 extends SugarWebServiceImplv4_1
SugarWebServiceImplv4_1:
class SugarWebServiceUtilv4_1_1 extends SugarWebServiceUtilv4_1
Change the require/include paths in:
registry.php rest.php
soap.php
SugarWebServiceImplv4_1_1.php
SugarWebServiceImplv4_1_1.php
so that they now point to the 4_1_1 directory.
Implement a method in SugarWebServiceImplv4_1_1.php.
function get_without_documents($session, $module_name = 'accounts', $start = 1, $offset = 20) {
$output_list = array();
$db = DBManagerFactory::getInstance();
// just for example
$sql_query = "SELECT a.id FROM accounts a WHERE a.id NOT IN (SELECT DISTINCT account_id FROM documents_accounts)";
$result = $db->limitQuery($sql_query, $start, $offset,false);
while(($row = $db->fetchByAssoc($result)) != null){
$output_list[] = $row['id'];
}
return array(
'result_count'=> count($output_list),
'next_offset' => $offset + $start,
'entry_list' => $output_list,
);
}
Finally register the method and it the types in registry.php and the reference the url so that it uses v4_1_1.

Laravel 4 list and detail route

I want to achieve the following:
devices > Controller#devices
devices/{id} > Controller#devices
Is this possible with Laravel ? I'm trying to map a domotic box with an android application ImperiHome, and they expect me to have the same route for devices list and for any device action.
So far I've tried this:
Route::get('devices/{deviceId}/action/{actionName}/{actionParam?}', 'DomoticzController#devices');
Route::get('devices', 'DomoticzController#devices');
But I cannot retrieve the argument when I call the devices/id url
Ok, so to solve the php strict standard error I just splitted the routes to two methods as follows:
routes.php
Route::get('devices/{deviceId}/action/{actionName}/{actionParam?}', 'DomoticzController#device');
Route::get('devices', 'DomoticzController#devices');
Route::get('rooms', 'DomoticzController#rooms');
//Route::get('action_ret', 'DomoticzController#action_ret');
Route::get('system', 'DomoticzController#system');
Route::get('/', 'DomoticzController#system');
DomoticzController.php
/**
* Call for an action on the device identified by $deviceId.
* #return string Json formated action status.
*/
public function device($deviceId, $actionName, $actionParam = null)
{
$client = $this->getClient();
$request = $client->getClient()->createRequest('GET', get_url("json.htm?type=command&param={$actionName}&idx={$deviceId}}&switchcmd=$actionParam"));
$response = $request->send();
$input = $response->json();
// convert to app format
$output = array('success' => ('OK' === $input['status'] ? true : false), 'errormsg' => ('ERR' === $input['status'] ? 'An error occured' : ''));
return Response::json($output);
}
/**
* Retrieve the list of the available devices.
* #return string Json formatted devices list.
*/
public function devices()
{
$client = $this->getClient();
$request = $client->getClient()->createRequest('GET', get_url('json.htm?type=devices&used=true'));
$response = $request->send();
$input = $response->json();
// convert to app format
$output = new stdClass();
$output->devices = array();
foreach ($input['result'] as $device) {
$output->devices[] = array (
'id' => $device['idx'],
'name' => $device['Name'],
'type' => 'DevSwitch',
'room' => null,
'params' => array(),
);
}
return Response::json($output);
}
maybe there is a better way to solve this, I would be glad to hear it.
If you let both routes use the same controller action, you need to make the parameters optional in the controller I think.
Try this public function device($deviceId = null, $actionName = null, $actionParam = null) and see if you still get the PHP strict error.
You can not have a route without parameters be redirected to a controller action that expects parameters. You can, on the other hand, make a route with parameters be redirected to a controller action with optional parameters (this does not mean that your route parameters need to be optional).

silverstripe dataobject searchable

I´m trying to have certain DataObjects (News) displayed in the default SearchResult Page. So the result should display normal Pages and News.
Is there an easy way to accomplish that in Silverstripe 3?
Or is it recommended to code it completely custom - I mean a custom controller/action which handles the search request and creates a result list, which I display then in a custom template?
I found this, but obviously search is disabled right now:
https://github.com/arambalakjian/DataObjects-as-Pages
Thx and regards,
Florian
I usually but together a custom search function after enabling FulltextSearchable. So in _config.php I would have
FulltextSearchable::enable();
Object::add_extension('NewsStory', "FulltextSearchable('Name,Content')");
replacing Name and Content with whatever DBField you want to be searchable. And each searchable DataObject have this in their class to enable search indexes (pretty sure this needs to be added and run dev/build before enabling the extension, and only works on MySQL DB).
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
);
then in my PageController I have my custom searchForm and results functions.
Here is the search function that returns the search form, called with $search in the template:
public function search()
{
if($this->request && $this->request->requestVar('Search')) {
$searchText = $this->request->requestVar('Search');
}else{
$searchText = 'Search';
}
$f = new TextField('Search', false, $searchText);
$fields = new FieldList(
$f
);
$actions = new FieldList(
new FormAction('results', 'Go')
);
$form = new Form(
$this,
'search',
$fields,
$actions
);
//$form->disableSecurityToken();
$form->setFormMethod('GET');
$form->setTemplate('SearchForm');
return $form;
}
and here the custom results function to handle the queries...
function results($data, $form, $request)
{
$keyword = trim($request->requestVar('Search'));
$keyword = Convert::raw2sql($keyword);
$keywordHTML = htmlentities($keyword, ENT_NOQUOTES, 'UTF-8');
$pages = new ArrayList();
$news = new ArrayList();
$mode = ' IN BOOLEAN MODE';
//$mode = ' WITH QUERY EXPANSION';
//$mode = '';
$siteTreeClasses = array('Page');
$siteTreeMatch = "MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keyword'$mode)
+ MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keywordHTML'$mode)";
$newsItemMatch = "MATCH( Name, Content ) AGAINST ('$keyword'$mode)
+ MATCH( Name, Content ) AGAINST ('$keywordHTML'$mode)";
//Standard pages
foreach ( $siteTreeClasses as $c )
{
$query = DataList::create($c)
->where($siteTreeMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $siteTreeMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record )
{
if ( in_array($record['ClassName'], $siteTreeClasses) )
$objects[] = new $record['ClassName']($record);
}
$pages->merge($objects);
}
//news
$query = DataList::create('NewsStory')->where($newsItemMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $newsItemMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record ) $objects[] = new $record['ClassName']($record);
$news->merge($objects);
//sorting results
$pages->sort(array(
'Relevance' => 'DESC',
'Title' => 'ASC'
));
$news->sort(array(
'Relevance' => 'DESC',
'Date' => 'DESC'
));
//output
$data = array(
'Pages' => $pages,
'News' => $news,
'Query' => $keyword
);
return $this->customise($data)->renderWith(array('Search','Page'));
}
I add all the Page classes I want to be searched and that extend SiteTree in the $siteTreeClasses array, and the News parts can be pretty much copied for any other DataObjectI need searchable.
I am not saying this is the best solution and this can definitely be improved on, but it works for me and this might be a good stating point.
I have adapted #colymba's solution into a silverstripe module: https://github.com/burnbright/silverstripe-pagesearch
It allows setting the pagetype in the url.
You'll need to substantially overwrite SearchForm->getResults().
It uses Database->searchEngine(), but those are tailored towards SiteTree and Page classes.
The "proper" solution is to feed the data into a search engine like Solr or Sphinx.
We have the SS3-compatible "fulltextsearch" module for this purpose:
https://github.com/silverstripe-labs/silverstripe-fulltextsearch
It's going to take some upfront setup, and is only feasible if you can either host Solr yourself, or are prepared to pay for a SaaS provider. Once you've got it running though, the possibilities are endless, its a great tool!

Yii CJuiAutoComplete data from Ajax request

I have a task form for an application I am building which allows me to specify what contact it is assigned to. I had set up a dependent dropdown in Yii with the following code:
echo $form->dropDownList($model,'associationType',
array('none'=>'None','contact'=>'Contact','sale'=>'Sale','account'=>'Account',
'project'=>'Project','case'=>'Case'),
array(
'ajax' => array(
'type'=>'POST', //request type
'url'=>CController::createUrl('tasks/parseType'), //url to call.
//Style: CController::createUrl('currentController/methodToCall')
'update'=>'#auto_complete', //selector to update
)
));
What I'm trying to do is use the CJuiAutoComplete widget with the dropDown specifying which array to grab. So the drop down is selected to contacts, it should get a list of contacts etc.
Here is what I have for the CJui widget
$this->widget('zii.widgets.jui.CJuiAutoComplete', array(
'name'=>'auto_select',
'source' => $names,
'options'=>array(
'minLength'=>'2',
'select'=>'js:function( event, ui ) {
$("#'.CHtml::activeId($model,'associationId').'").val(ui.item.id);
return false;
}',
),
));
The variable $names is just a placeholder for now, but in my controller method I pass it a JSON encoded array with id and name. Controller code:
public function actionParseType() {
//if(isset($_POST['TaskChild']['associationType'])){
//$type=$_POST['TaskChild']['associationType'];
$type='sale';
$sql = 'SELECT id, name FROM x2_'.$type.'s';
$cmd = Yii::app()->db->createCommand($sql);
$res = $cmd->queryAll();
echo CJSON::encode($res);
//}
}
Right now I'm forcing it to use "Sale" but I don't get anything when I call the method, and was wondering how I might go about fixing this. I'm still a little new to Yii so I've been mostly reading wiki/forum posts on how these kinds of thing are done. Any help is greatly appreciated, thanks!
Try something like this in your controller action:
$sql = 'SELECT people_id as id, CONCAT(first_name," ",last_name) as value, first_name as label FROM people WHERE first_name LIKE :qterm ORDER BY first_name ASC';
$command = Yii::app()->db->createCommand($sql);
$qterm = $_GET['term'].'%';
$command->bindParam(":qterm", $qterm, PDO::PARAM_STR);
$result = $command->queryAll();
echo CJSON::encode($result); exit;
Then you can check it by using this in your widget 'options' array: 'select'=>'js:function(event, ui) { console.log(ui.item.id +":"+ui.item.value); }'

Facebook New PHP SDK For Graph API - Multi Query

I'm at my wits end in what the queries parameter value should look like. So to sumbit a multiquery accross the graph API library, the following would be the code method to perform it, as far as I can tell.
$param = array(
'method' => 'fql.multiquery',
'queries' => $multiQuery,
'callback' => '');
$queryresults = $facebook->api($param);
Using this method in the new PHP SDK library from Facebook, has any one made this work? If so can you drop an example on how you build the complete value of the $multiQuery variable?
I've been struggeling with this for a few days and I'm only finding exmaples with the old PHP library.
Why is it always after banging your head for days, you ask a question, and 5 minutes later, you come up with the answer your self.
So here was MY lovely experience.
Since in PHP you can use a "/' character to start a text string, I got my self stuck in the flip flopping of the double quote character and single quote character. It dawned on me that the queries defined in a multi query are, duh, wrapped by double quotes.
So lesson learned? If you have a where clause that uses a string value in a multi query, make sure for pete's sake you use SINGLE QUOTES around the string value your filtering on.
BAD BAD - This is what I did. note the double quotes around myvalue and myothervalue. NAUGHTY!
$multiQuery = {
"query1":"select something from something where somecolumn = "myvalue"",
"query2":"select something from something where somecolumn = "myothervalue""
};
GOOD Example - Now look at myvalue and myothervalue.
$multiQuery = {
"query1":"select something from something where somecolumn = 'myvalue'",
"query2":"select something from something where somecolumn = 'myothervalue'"
};
So now I can...
$multiQuery = {
"query1":"select something from something where somecolumn = 'myvalue'",
"query2":"select something from something where somecolumn = 'myothervalue'"
};
$param = array(
'method' => 'fql.multiquery',
'queries' => $multiQuery,
'callback' => '');
$queryresults = $facebook->api($param);
And if any of you are wondering what is the actual type of the $multiQuery variable is (for newbie like me), it's just a string data type. It's not an array, nothing more nifty than text.
Considering an array of node id's with their respective url's as values you'll have
/**
*A JSON-encoded dictionary of the queries to perform. The array contains a set of key/value pairs.
*Each key is a query name, which can contain only alphanumeric characters and optional underscores.
*Each key maps to a value containing a traditional FQL query.
*/
$fql = '{';
foreach ($path as $key1 => $value1) {
$fql .= '"' . $key1 . '":"SELECT share_count, like_count, comment_count, total_count FROM link_stat WHERE url=\'' . $value1 . '\'",';
}
$fql .= '}';
$param = array(
'method' => 'fql.multiquery',
'queries' => $fql,
'callback' => ''
);
try {
$fqlresult = $facebook->api($param);
} catch (FacebookApiException $e) {
watchdog('Facebook Query', 'Parsing error on node #node | #error', array('#node' => $key1, '#error' => $e), WATCHDOG_DEBUG); }
You can try this:
$multiQuery= array ("query1" => "query #1 goes here","query2" => "query #2 goes here");
$param = array(
'method' => 'fql.multiquery',
'queries' => $multiQuery,
'callback' => '');
$queryresults = $facebook->api($param);