A morning of googling I've learnt a few things.
Many people would like to use their CI2 installs to make a soap server for other apps to talk to their app.
However, each forum post I find ends in 'This doesn't work in CI2' or similar.
I've found this article:
http://phpmaster.com/web-services-with-php-and-soap-1/
Which is GREAT... but I can't get it working in CI2.
I've put my library in place, renamed where needed and this code is bringing up many errors.
I've not started on the client side yet.
`class soap extends CI_Controller {
function __construct ()
{
parent:: __construct ();
$this->load->library('nusoap_base');
}
function index()
{
$this->nusoap = new soap_server();
$this->nusoap->register("getProd");
$this->nusoap->service($HTTP_RAW_POST_DATA);
}
function getProd($category) {
if ($category == "books") {
return join(",", array(
"The WordPress Anthology",
"PHP Master: Write Cutting Edge Code",
"Build Your Own Website the Right Way"));
}
else {
return "No products listed under that category";
}
}
}`
Got fed up, using CI REST library instead (cheers Phil!)
:)
Related
public function actionDone($id)
{
if ($model = $this->findModel($id)) {
$model["status"] = 3;
if ($model->save()) {
return $this->redirect(['test/index']);
}
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
It works only for the first time for each link. After that its just redirects to the 'test/index' without doing anything. Seems like browser (or smth else) remember, that if we open, for example, page site.com/?r=test/done&id=2 it should redirect to 'test/index' anyway.
Why is that? How can I fix it?
I even tried put die(); in the beginning of the method - anyway it redirects to 'test/index' until I use different link with another ID.
Thanks!
I've developed a CI site on my local machine using WAMP. I'm using CI 3 with the HMVC extension and it all works fine.
I've just uploaded the site to the production server and changed the config files etc to get it working. However, form validation callbacks are not working on the production server.
Here's an example from a login form:
// Process login form
public function submit()
{
$this->load->library('form_validation');
$username = $this->input->post('username', TRUE);
$this->form_validation->set_rules('username', 'Username', 'required');
$this->form_validation->set_rules('password', 'Password', 'required|callback_do_login');
if($this->form_validation->run($this) == FALSE)
{
$this->login();
}
else
{
// Set login session
...
}
}
This is the callback function do_login:
public function do_login($password)
{
// Get user / pass from POST
$submitted_password = $this->input->post('password', TRUE);
$username = $this->input->post('username', TRUE);
$this->crud->setTable($this->table);
$this->load->model('mdl_users');
// Check User Exists
$query = $this->mdl_users->getUser($username);
if($query->num_rows() == 1)
{
// Get stored password
$row = $query->row();
$stored_password = $row->password;
// Check password
$this->load->module('mod_security');
$result = $this->mod_security->login($username, $submitted_password, $stored_password); // Returns false if no match
return ($result) ? TRUE : FALSE;
}
else
{
return FALSE;
}
}
On my local WAMP setup, this all works fine. But on the server
$this->form_validation->run($this)
always returns false.
To eliminate any errors with the callback function itself, I changed it to the following as a test:
public function do_login($password)
{
return TRUE;
}
... which will obviously always return TRUE, however when $this->form_validation->run($this) is called, even though I changed the do_login callback function to return TRUE, $this->form_validation->run($this) still returns FALSE!!
This is driving me crazy and I have no idea why this is happening. It is as if the form validation is ignoring the callback function and just returning false.
Can anyone help me? Could it be a setting on the server causing it or something else that could have changed when I uploaded the site files to the server?
If it is relevant, on my local machine, within the do_login callback function I had originally set the callback error message like this:
$this->form_validation->set_message('do_login', 'User / Pass Incorrect');
...which worked fine, but on the production server it threw an error stating: "Unable to access an error message corresponding to your field name Password.(do_login)". I had to set the error message in the language library file to overcome this. But the fact that this happened on the production server and not on my WAMP setup makes me think there must be some setting or something on the server that is causing this.
Anyway, any help is gratefully received.
Thanks
I finally found the solution. The HMVC application requires a small library file named as MY_Form_validation.php. Seemingly everywhere on the net this file is shown to be thus:
class MY_Form_validation extends CI_Form_validation {
function run($module = '', $group = '')
{
(is_object($module)) AND $this->CI = &$module;
return parent::run($group);
}
}
However, although this worked in my WAMP setup, it did not work on my server. I found that by declaring the CI variable at the start the server problem is resolved. So the MY_Form_validation.php file finally becomes:
class MY_Form_validation extends CI_Form_validation {
public $CI;
function run($module = '', $group = '')
{
(is_object($module)) AND $this->CI = &$module;
return parent::run($group);
}
}
I get to the weird situation. I have up to date server files with local files. This "flash message with error" appears when this is not valid:
if ($form->isValid() && $this->checkLastComment($commentRepository,$user,$status, $comment)) {
I have two urls. First /Home (working) for everyone where i load statuses with comments BUT I DO NOT CACHE THE PAGE
Then i have /Suggested url where i load statuses with comments BUT USING
$response->setPublic();
$response->setSharedMaxAge(3600);
I CACHE THE PAGE because its the same for all users.
But its weird because on local machine (caching is on i tested) everything runs normal when i want to create a comment... on prod, dev env.
On server it runs normal when i am under dev env.(caching is off) but when i try post comment on prod env. i get error flash message for the mentioned condition...
WTF? Where could be leak please? i have no idea.
The public esi cache somehow breaks my forms? or...?
One friend is able to post a comment there... another one is not.. weird... i wasn't before but after cache clear i am again able...
EDIT:
After lunch i tried it again and i am not able to post comment... wtf..
This is my header i see in chrome: (sending)
CommentForm[comment]:gllll
status_id:65084
CommentForm[_token]:4858119eccbc91da6219d4cbaa1b6c2e79dbd56a
comment_id:0
Using this jquery code:
var url=Routing.generate('create_comment', {"comment_id": comment_id_value, "status_id": status_id_value})+'.json';
$.post(url, $this.serialize(), function(data) {
To this controller:
public function createAction(Request $request, $comment_id=0, $status_id)
{
// first CHECK if user exists
$user=$this->getUser();
if ($user) {
$em=$this->getDoctrine()->getManager();
// GET REPOSITORIES
$statusRepository=$em->getRepository('WallBundle:Status');
$commentRepository=$em->getRepository('WallBundle:Comments');
$notifyRepository=$em->getRepository('NotifyBundle:Notify');
$userRepository=$em->getRepository('UserBundle:User');
// GET SM
$SM=$this->get('status_manager');
// GET status by ID
$status=$statusRepository->find($status_id);
// CHECK if this status exists
if ($status) {
$targetUser=$status->getUser();
if ($request->isMethod('POST') && ($this->getRequest()->getRequestFormat() == 'json')) {
if ($comment_id==0 || !$cE) {
$cE = new Comments();
}
$form = $this->createForm(new CommentFormType(), $cE);
$form->bind($request);
$comment=$form->getData()->getComment();
if ($form->isValid() && $this->checkLastComment($commentRepository,$user,$status, $comment)) {
AND the checkLastComment function
public function checkLastComment($commentRepository, User $user,Status $status, $comment)
{
// check if last comment was more than 10s ago
$lastCommentQueryArray=$commentRepository->getLastComment($user, $status);
$lastCommentTime=0;
$lastCommentContent='';
foreach ($lastCommentQueryArray as $lastComment) {
$lastCommentTime =$lastComment['time'];
$lastCommentContent=$lastComment['comment'];
}
if (($lastCommentTime+10>=time()) && (trim($lastCommentContent)==trim($comment))) {
return false;
}
else {
return true;
}
}
*But the bug should not be in the code because i am using this technique all over the web and everything runs good... only at this particularly page ITS NOT WORKING ... and the only difference between pages is that this one is cached ... + when i am creating a new comment it has nothing with cache isn't that right? it only takes the data from form which is on cached page... *
I had a small test done in PHP for a Controller I had written in Symfony2:
class DepositControllerTest extends WebTestCase {
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'POST',
'/deposit',
array( "amount" => 23),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
}
Up to here, everything was great. Problem started when I realized I wanted to disable possible re-submissions while refreshing the page. So I added a small mechanism to send nonce on every submission.
It works something like this:
class ReplayManager {
public function getNonce() {
$uid = $this->getRandomUID();
$this->session->set("nonce", $uid);
return $uid;
}
public function checkNonce($cnonce) {
$nonce = $this->session->get("nonce");
if ($cnonce !== $nonce)
return false;
$this->session->set("nonce", null);
return true;
}
}
So I had to mofidy the controller to get the nonce when displaying the form, and consume it when submitting.
But now this introduces a problem. I cant make a request to POST /deposit because I dont know what nonce to send. I thought to requesting first GET /deposit to render the form, and setting one, to use it in the POST, but I suspect Symfony2 sessions are not working in PHPUnit.
How could I solve this issue? I would not want to go to Selenium tests, since they are significant slower, not to mention that I would have to rewrite A LOT of tests.
UPDATE: I add a very simplified version of the controller code by request.
class DepositController extends Controller{
public function formAction(Request $request){
$this->replayManager = $this->getReplayManager();
$context["nonce"] = $this->replayManager->getNonce();
return $this->renderTemplate("form.twig", $context);
}
protected function depositAction(){
$this->replayManager = $this->getReplayManager();
$nonce = $_POST["nonce"];
if (!$this->replayManager->checkNonce($nonce))
return $this->renderErrorTemplate("Nonce expired!");
deposit($_POST["amount"]);
return $this->renderTemplate('confirmation.twig');
}
protected function getSession() {
$session = $this->get('session');
$session->start();
return $session;
}
protected function getReplayManager() {
return new ReplayManager($this->getSession());
}
}
I'm not sure what ReplayManager does, but it looks to me as if it is not the right class to handle the 'nonce'. As the 'nonce' is ultimately stored in and retrieved from the session it should either be handled by the controller or abstracted out into its own class which is then passed in as a dependency. This will allow you to mock the nonce (sounds like a sitcom!) for testing.
In my experience problems in testing are actually problems with code design and should be considered a smell. In this case your problem stems from handling the nonce in the wrong place. A quick refactoring session should solve your testing problems.
It is possible to access the Symfony2 session from PHPUnit via the WebTestCase client. I think something like this should work:
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'GET',
'/deposit',
);
$session = $this->client->getContainer()->get('session');
$nonce = $session->get('nonce');
$this->crawler = self::$client->request(
'POST',
'/deposit',
array("amount" => 23, "nonce" => $nonce),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
EDIT:
Alternatively, if there is a problem getting the nonce value from the session, you could try replacing the two lines between the GET and POST requests above with:
$form = $crawler->selectButton('submit');
$nonce = $form->get('nonce')->getValue(); // replace 'nonce' with the actual name of the element
for a web application (an Image Database) I am using the Restful Server Module. The Data is requested by another web application (a shop). The generation of the XML takes up to 1 second. the shop has to wait for the API to answer to display for example a Product Page. Is it possible to activate some caching for the Restful Server API?
I tried already with the Static Publisher but it seems to work just with cms pages.
Many thanks,
Florian
RestfulService does the caching for you. It accepts 2 paramaters. A serviceURL and also a cache time. The default is 3600 (1 hour). This is only going to work if the shop is built with silverstripe.
$serviceURL = 'http://www.imagedatabase.com/api/v1/Product?ID=1';
$service = new RestfulService($serviceURL, 7200); //2 hours expiry
$xml = $service->request()->getBody();
//get fields values
$productName = $service->searchValue($xml, 'Name');
$productPrice = $service->searchValue($xml, 'Price');
You also need to make a modification to Product assuming Product is a dataobject.
class Product extends DataObject {
...
static $api_access = true;
...
function canView($member = null) {
return true;
}
}
RestfulService docs
http://doc.silverstripe.org/framework/en/reference/restfulservice
well, I personally would cache on the client (so in the shop)
but if you have to, I don't think there is any built in way for this.
you could subclass the restful server and do some basic caching yourself (just the way the default SS RestfulClient does it, save it into a file)
class MyServer extends RestfulServer {
public $cache_expire;
function __construct($cache_expire = 3600) {
$this->cache_expire = $cache_expire;
}
protected function getHandler($className, $id, $relationName) {
$cache_path = Director::getAbsFile("assets/rest-cache/$className-$id-$relationName.{$this->request->getExtension()}");
if ($this->cache_expire > 0 && !isset($_GET['flush'])
&& #file_exists($cache_path) && #filemtime($cache_path) + $this->cache_expire > time()
) {
$store = file_get_contents($cache_path);
$response = unserialize($store);
} else {
$response = parent::getHandler($className, $id, $relationName);
$store = serialize($response);
file_put_contents($cache_path, $store);
}
return $response;
}
}
// NOTE, I have never tested this code, so you might run into minor spelling mistakes or something like that