So am experimenting with Webix and so far I like it. Am playing around with the mobile calendar/scheduler and am getting stuck with saving new events into mysql database. I have followed the tutorials and docs about saving data to the database and indeed I am able to save new events. Problem is that it is saving the new events twice, and I have failed to figure out why. Here is the code:
webix.ready(function(){
webix.ui.fullScreen();
webix.ui({
rows:[
{
view: "toolbar", id:"toolbar", elements:[
{
view: "icon", icon: "bars",
click: function(){
if( $$("menu").config.hidden){
$$("menu").show();
}
else
$$("menu").hide();
}
},
{
view: "label",
label: "JPlan"
}
]
},
{
view: "scheduler",
id: "scheduler",
url: "data/getEvents.php",
save:{
"insert":"data/saveEvents.php",
"update":"data/update.php",
"delete":"data/delete.php"
}
}
]
});
});
And the php code to save the new events:
<?php
include('mysql.class.php');
$db = new MySQL();
$values = array();
$now = time();
$values["eventTitle"] = MySQL::SQLValue($_POST['text']);
$values["eventJudge"] = MySQL::SQLValue($_POST['judge']);
$values["eventStart"] = MySQL::SQLValue($_POST['start_date']);
$values["eventEnd"] = MySQL::SQLValue($_POST['end_date']);
$values["eventNotes"] = MySQL::SQLValue($_POST['details']);
$values["eventAddedTime"] = MySQL::SQLValue($now);
$values["eventAddedBy"] = MySQL::SQLValue('');
// Execute the insert
$result = $db->InsertRow("tbl_events", $values);
?>
The app loads all events from the database well. It is just that when I save a new event, it saves the evet twice in the database. I have not yet worked on the update and delete code.
Any help?
Client side code expects some valid JSON response from a server-side, as confirmation that the save operation was successful. Otherwise, a client-side code may make another attempt to save a data.
$result = $db->InsertRow("tbl_events", $values);
$id = get_id_of_new_record();
echo "{ id: $id }";
I am developing a CRUD system in Phalcon PHP (version 1.3.4).
My goal is to create a link (delete row), that asks for confirmation on click (JavaScript confirmation box) and then goes (request type POST) to the link.
So lets say a user clicks on the "delete row" button.
JavaScript confirmation "Are you sure you want to delete this row?"
User clicks "yes"
Webpage does a POST to "/users/delete/1"
I know CakePHP has a function (FormHelper::postLink()) that does exactly that.
I was wondering if Phalcon PHP also had a function like this.
I see three possibilities to achieve what you want. One is to create a macro in Volt template, second is to add a function to your View. Third and closest to - what I understand is your wish - is to extend Phalcons tag helper and this is part I will describe here.
Phalcon has its own Tag helper to allow you to easily create some elements. postLink is not a part that is implemented there, but you can easily achieve it. In my example I have namespace of Application with class of Tag that extends from \Phalcon\Tag. This is my base for this tutorial.
// Tag.php
namespace Application;
class Tag extends \Phalcon\Tag
{
static public function postLink() {
return '<strong>TEST TAG</strong>';
}
}
To force Phalcon DI to use this class, it is necessary to override it's standard declaration from engine by declaring it by hand as a new DI service:
// services.php
$di['tag'] = function() {
return new \Application\Tag();
};
You can test if it is working properly by typing {{ tag.postLink() }} in Volt template or with $this->tag->postLink() if using phtml template.
Now you can fill your Tag::postLink() method with HTML and parameters you wish it will produce:
namespace Application;
class Tag extends \Phalcon\Tag
{
static $forms = [];
static public function postLink($title, $url, $options = array()) {
// random & unique form ID
while ($randId = 'f_' . mt_rand(9000, 999999)) {
if (!isset(self::$forms[$randId])) {
self::$forms[$randId] = true;
break;
}
}
// dialog message
$dialogMessage = isset($options['message']) && $options['message'] ? $options['message'] : 'Are you sure you want to go on?';
$html = <<<HTML
<form action="{$url}" method="post" id="{$randId}">
<!-- maybe not necessary part -->
<input type="hidden" name="confirmed" value="1" />
</form>
{$title}
HTML;
return $html;
}
}
Now you can run it like this:
{{ tag.postLink('delete', '/users/delete/1') }}
{% set formOptions = ['message' : 'Are you sure you want to delete user Kialia Kuliambro?'] %}
{{ tag.postLink('delete', '/users/delete/1', formOptions) }}
{{ tag.postLink('delete', '/users/delete/1', ['message' : 'Are you sure you want to delete user Kialia Kuliambro?']) }}
Have fun extending :)
There's a few ways to implement such behavior in phalcon. Before anything, we need to understand how views and view helpers work in phalcon. And if you pay close attention, you'll notice, both .volt and .phtml have direct access to the DI.
In volt, for example, you can access the flash service, and output its messages by calling:
{{ flash.output() }}
which gets converted to the phtml: <?php echo $this->flash->output(); ?>
Thus my solution focuses on defining a new service in the DI which volt can access. In CakePHP, the syntax for postLink(), looks something like: echo $this->Form->postLink() while the function is actually defined in a class named FormHelper. So my solution will do the same thing, define a class FormHelper, then inject it into the view under the name Form.
Create an app/helpers/ directory.
Update your app/config/config.php file adding a reference to our new directory: 'helpersDir'=> APP_PATH . '/app/helpers/'
Update your app/config/loader.php file adding $config->application->helpersDir to the registered directories.
Create a new file app/helpers/FormHelper.php
Copy-paste the following code into the file:
<?php
use Phalcon\Tag;
class FormHelper extends Tag
{
protected $_lastAction = '';
public function dottedNameToBracketNotation($name)
{
$parts=explode('.',$name);
$first = array_shift($parts);
$name=$first . ($parts ? '[' . implode('][', $parts) . ']' : '');
return $name;
}
protected function flatten(array $data, $separator = '.')
{
$result = [];
$stack = [];
$path = null;
reset($data);
while (!empty($data)) {
$key = key($data);
$element = $data[$key];
unset($data[$key]);
if (is_array($element) && !empty($element)) {
if (!empty($data)) {
$stack[] = [$data, $path];
}
$data = $element;
reset($data);
$path .= $key . $separator;
} else {
$result[$path . $key] = $element;
}
if (empty($data) && !empty($stack)) {
list($data, $path) = array_pop($stack);
reset($data);
}
}
return $result;
}
protected function _confirm($message, $okCode, $cancelCode = '', $options = [])
{
$message = json_encode($message);
$confirm = "if (confirm({$message})) { {$okCode} } {$cancelCode}";
if (isset($options['escape']) && $options['escape'] === false) {
$confirm = $this->h($confirm);
}
return $confirm;
}
public function h($text, $double = true, $charset = 'UTF-8')
{
return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset, $double);
}
protected function _lastAction($url)
{
$action = $url;//Router::url($url, true);
$query = parse_url($action, PHP_URL_QUERY);
$query = $query ? '?' . $query : '';
$this->_lastAction = parse_url($action, PHP_URL_PATH) . $query;
}
public function postLink($title, $url = null, array $options = [])
{
$out='';
$options += ['block' => null, 'confirm' => null];
$requestMethod = 'POST';
if (!empty($options['method'])) {
$requestMethod = strtoupper($options['method']);
unset($options['method']);
}
$confirmMessage = $options['confirm'];
unset($options['confirm']);
$formName = str_replace('.', '', uniqid('post_', true));
$formOptions = [
'name' => $formName,
'style' => 'display:none;',
'method' => 'post',
];
if (isset($options['target'])) {
$formOptions['target'] = $options['target'];
unset($options['target']);
}
$formOptions[0]=$url;
$out.=$this->form($formOptions);
$out .= $this->hiddenField(['_method','value' => $requestMethod]);
$fields = [];
if (isset($options['data']) && is_array($options['data'])) {
foreach ($this->flatten($options['data']) as $key => $value) {
$out .= $this->hiddenField([$this->dottedNameToBracketNotation($key),'value' => $value]);
}
unset($options['data']);
}
$out .= $this->endForm();
//This is currently unsupported
if ($options['block']) {
if ($options['block'] === true) {
$options['block'] = __FUNCTION__;
}
//$this->_View->append($options['block'], $out);
$out = '';
}
unset($options['block']);
$url = '#';
$onClick = 'document.' . $formName . '.submit();';
if ($confirmMessage) {
$options['onclick'] = $this->_confirm($confirmMessage, $onClick, '', $options);
} else {
$options['onclick'] = $onClick . ' ';
}
$options['onclick'] .= 'event.returnValue = false; return false;';
$options[0]=$url;
$options[1]=$title;
$options[2]=false;
$out .= $this->linkTo($options);
return $out;
}
}
Edit your app/config/services.php file and add in:
$di->set('Form',function () {
return new FormHelper();
});
(you could make "Form" lowercase if you want, both work. I made it capital to closer resemble CakePHP's syntax. Do note that Volt is case sensitive when trying to access services but phtml will lowercase it.)
Edit the template you want to test the code on, such as app/views/index/test.volt
Copy-paste the following code into there:
{{ Form.postLink(' Delete','',['confirm':'Are you sure you want to delete #4?','data':['a':['b','c']]]) }}
Alternatively for phtml, use: <?php echo $this->form->postLink(' Delete', '', array('confirm' => 'Are you sure you want to delete #4?', 'data' => array('a' => array('b', 'c')))); ?>
Run it, and watch it work its magic, just render your index/test.volt template by visiting /index/test in your address bar. (Make sure you defined such an action in your index controller)
In terms, of other solutions, you could also use $compiler->addFunction() to make functions available to volt, one at time. The page in the manual gives the example of $compiler->addFunction('shuffle', 'str_shuffle');. You can attempt to override the factoryDefault for "tag" in the DI, and use the helper we already defined which extends tag. So you'd just change it from "form" to "tag" like so: $di->set('tag',function () {return new FormHelper();}); but, as you can see, it won't make the function postLink() available to volt as a function, you'll notice you still need to access it as tag.postLink(). Rather, all the \Phalcon\Tag functions are actually hard-coded into the volt engine. You can see this clearly by viewing the zephir source code of the \Phalcon\Mvc\View\Engine\Volt\Compiler class available over here. For your convenience, and in case the link ever gets broken, I have posted a snippet here which shows the "tag" functions in volt are actually hard-coded into it:
if method_exists(className, method) {
let arrayHelpers = this->_arrayHelpers;
if typeof arrayHelpers != "array" {
let arrayHelpers = [
"link_to": true,
"image": true,
"form": true,
"select": true,
"select_static": true,
"submit_button": true,
"radio_field": true,
"check_field": true,
"file_field": true,
"hidden_field": true,
"password_field": true,
"text_area": true,
"text_field": true,
"email_field": true,
"date_field": true,
"tel_field": true,
"numeric_field": true,
"image_input": true
];
let this->_arrayHelpers = arrayHelpers;
}
if isset arrayHelpers[name] {
return "$this->tag->" . method . "(array(" . arguments . "))";
}
return "$this->tag->" . method . "(" . arguments . ")";
}
So, if you'd like to "hack" in a few more methods by extending the \Phalcon\Tags class, you're out of luck. However, as demonstrated on the volt documentation page, there exists the concept of registering custom extensions to work with volt. The documentation gives the example of: $compiler->addExtension(new PhpFunctionExtension());
Where the source of the class is:
<?php
class PhpFunctionExtension
{
/**
* This method is called on any attempt to compile a function call
*/
public function compileFunction($name, $arguments)
{
if (function_exists($name)) {
return $name . '('. $arguments . ')';
}
}
}
This would allow volt access to any function you'd like, without having to manually register every possible function you could possibly ever need. You can test this by trying to access str_shuffle in volt, like we did before with $compiler->addFunction('shuffle', 'str_shuffle'); but this time without having to register it.
In terms of other solutions, you could also try to integrate CakePHP and PhalconPHP together, and attempt to call CakePHP's view helpers from PhalconPHP, but then you'd run into a problem of CakePHP not understanding your router setup you have configured in Phalcon. But, if you're determined, you could code all the routes and config for CakePHP and run it alongside PhalconPHP, but I'd highly discourage such a desperate workaround. And, finally, if you understand how the function works, and you barely use it, you could get away with just hard-coding the HTML in the first place. Honestly, CakePHP's logic doesn't look so sound to me in the first place because it has to corrupt your HTML document with a form inserted which can bother your layout. I think it would make more sense to generate a form dynamically with JavaScript, if we're using JavaScript already, and append it to the <body> when the button is clicked, then submit the form we just created dynamically. But, you wanted a CakePHP implementation, so I coded it as close to the logic they used as possible. It's not perfect, in terms of supporting all their features, such as block, but it should suit most of your needs.
I can always revise my implementation, but I think it demonstrates how to work with Phalcon pretty well for those migrating from CakePHP.
I have a template that I am loading from a route like so:
this.route('formEdit', {
path: '/admin/form/:_id',
data: function() { return Forms.findOne({_id: this.params._id}); },
onBeforeAction: function() { AccountUtils.authenticationRequired(this, ['ADMIN']); }
});
In which I have a template defined like:
<template name="formEdit">
<div id="formContainer">
...
{{#each header_fields}}
<div class="sortable">
{{> headerFieldViewRow }}
</div>
{{/each}}
</div>
</template>
And:
<template name="headerFieldViewRow">
{{#with header_field}}
...
{{/with}}
</template>
I then make the container around all the header fields sortable using jQuery UI Sortable:
Template.formEdit.rendered = function() {
$('.sortable').sortable({
axis: "y",
stop: function(event, ui) {
var form = Blaze.getData($('#formContainer')[0]);
var newFormHeaders = [];
$('#headerFieldsTable div.headerField').each(function(idx, headerFieldDiv) {
var header = Blaze.getData(headerFieldDiv);
header.sequence = idx;
Meteor.call('saveHeaderField', header);
newFormHeaders.push({header_field_id: header._id});
});
form.header_fields = newFormHeaders;
Meteor.call('saveForm', form);
}
});
}
Basically, when sorting stops, loop through all the headers, getting the data for each and updating the sequence number, then re-build the array in Forms and save them back. In the server code I have printouts for the two save calls, and the do properly print out the correct order of both the headers and the form.
The problem I am running into is that, after sorting, the visual display of the form and it's headers "snaps" back to the pre-sorted state, even though the data in the DB is correct. If I simply reload the form, either by hitting enter in the Address bar or by simply re-loading it from the menu, everything is displayed correctly. It's as if the reactive piece isn't working.
I have noted that I am getting an error when I update the client code in my server log that reads:
=> Client modified -- refreshing
I20141010-18:25:47.017(-4)? Failed to receive keepalive! Exiting.
=> Exited with code: 1
I don't think this is related as I was getting that error prior to adding this sorting code.
Update: Adding code for saveForm and saveHeader
saveForm:
// Saves the Form to the DB
saveForm: function(params) {
// Structure the data to send
var formEntry = {
title: params.title,
client_id: params.client_id,
header_fields: params.header_fields,
form_fields: params.form_fields,
created_by: Meteor.userId(),
created_on: new Date()
};
if (params._id == null) {
console.log("Saving new Form entry: %j", formEntry);
formEntry._id = Forms.insert(formEntry);
} else {
formEntry._id = params._id;
console.log("Updating Form entry: %j", formEntry);
Forms.update({_id: formEntry._id}, formEntry);
}
return formEntry;
}
saveHeader:
// Saves the HeaderField to the DB
saveHeaderField: function(params) {
// Structure the data to send
var headerFieldEntry = {
label: params.label,
field_type: params.field_type,
field_options: params.field_options,
form_id: params.form_id,
required: params.required,
allows_pictures: params.allows_pictures,
record_geo: params.record_geo
};
if (params._id == null) {
console.log("Saving new HeaderField entry: %j", headerFieldEntry);
headerFieldEntry._id = HeaderFields.insert(headerFieldEntry);
} else {
headerFieldEntry._id = params._id;
console.log("Updating HeaderField entry: %j", headerFieldEntry);
HeaderFields.update({_id: headerFieldEntry._id}, headerFieldEntry);
}
return headerFieldEntry;
}
I think the issue here is that Meteor.call will run on the server - you either need to use a callback or invalidate your template, if you want to return a value Meteor.call. From the docs:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
There is more info in this answer and this answer and the Meteor.call docs.
Hope that helps!
I want to test a website which has a dynamic menustructure. I want to loop through all menuitems and run the same series of test on every page. We're talking about 100+ pages that change reguraly.
I would like to do this with either behat or codeception.
Does anybody have an idea about how to do this?
When using Behat with Mink, you can grab your menu items with findAll() and then iterate over it:
/**
* #When /^I run my test series for all menu items$/
*/
public function iRunMyTestSeriesForAllMenuItems() {
$result = TRUE;
$this->getSession()->visit('http://www.example.com/');
$links = $this->getSession()->getPage()->findAll('css', '#menu ul li a');
foreach ($links as $link) {
$url = $link->getAttribute('href');
if (FALSE === $this->yourTestHere($url)) {
$result = FALSE;
}
}
return $result;
}
I had a similar use case where I wanted to visit all pages of a given site map making sure there isn't any dead links. I had the approach to dynamically generate an array of steps which is then returned and processed by Behat. I had to add an artificial step "I print out page" to make sure I can see on the console what page is currently tested.
/**
* #Then /^I should access all pages of site map "([^"]*)"$/
*/
public function iShouldAccessAllPagesOfSiteMap($selector) {
$page = $this->getSession()->getPage();
$locator = sprintf('#%s a', $selector);
$elements = $page->findAll('css', $locator);
$steps = array();
foreach ($elements as $element) {
/** #var \Behat\Mink\Element\NodeElement $element */
$steps[] = new Behat\Behat\Context\Step\When(sprintf('I print out page "%s"', $element->getAttribute('href')));
$steps[] = new Behat\Behat\Context\Step\When(sprintf('I go to "%s"', $element->getAttribute('href')));
$steps[] = new Behat\Behat\Context\Step\Then('the response status code should be 200');
}
return $steps;
}
/**
* #When /^I print out page "([^"]*)"$/
*/
public function iPrintOutThePage($page) {
$string = sprintf('Visiting page ' . $page);
echo "\033[36m -> " . strtr($string, array("\n" => "\n| ")) . "\033[0m\n";
}
Then my scenario looks as follows:
Scenario: my website has no "dead" pages
Given I am on "/examples/site-map/"
Then I should access all pages of site map "c118"
The whole Gist is here https://gist.github.com/fudriot/6028678
I have a site that need to get some data from a different sit that is using asp.net MVC/
The data to get loaded is from these pages:
http://charity.hondaclassic.com/home/totaldonations
http://charity.hondaclassic.com/Home/CharityList
This should be a no brainer but for some reason I get an empty response, here is my JS:
<script>
jQuery.noConflict();
jQuery(document).ready(function($){
$('.totalDonations').load('http://charity.hondaclassic.com/home/totaldonations');
$('#charityList').load('http://charity.hondaclassic.com/home/CharityList');
});
</script>
in firebug I see the request is made and come back with a response of 200 OK but the response is empty, if you browse to these pages they work fine! What the heck?
Here are the controller actions from the MVC site:
public ActionResult TotalDonations() {
var total = "$" + repo.All<Customer>().Sum(x => x.AmountPaid).ToString();
return Content(total);
}
public ActionResult CharityList() {
var charities = repo.All<Company>();
return View(charities);
}
Someone please out what stupid little thing I am missing - this should have taken me 5 minutes and it's been hours!
The same origin policy prevents loading HTML from another web site via AJAX. The right way to do this would be to have the methods detect if the request is coming from AJAX and return JSONP instead.
public ActionResult TotalDonations( string callback )
{
var total = "$" + repo.All<Customer>().Sum(x => x.AmountPaid).ToString();
if (!string.IsNullOrEmpty(callback))
{
return Content( callback + "( { total: " + total + " } );" );
}
else
{
return Content(total);
}
}
...
$.getJSON('http://charity.hondaclassic.com/home/totaldonations?callback=?',
function(data) {
$('.totalDonations').html( data.total );
});
your totaldonations link is missing the o in total
> $('.totalDonations').load('http://charity.hondaclassic.com/home/ttaldonations');
should be
$('.totalDonations').load('http://charity.hondaclassic.com/home/totaldonations');
I ended up just doing it server side to avoid the same origin policy mentioned above:
Dim totalDonations As String
Dim charities As String
Using Client As New System.Net.WebClient()
totalDonations = Client.DownloadString("http://charity.hondaclassic.com/home/totaldonations")
charities = Client.DownloadString("http://charity.hondaclassic.com/home/CharityList")
End Using
worked like a charm.