Zend Form TextArea with countdown characters and StringLength Validator - different calc - zend-framework

In my form i have a zend_form_element_textarea. For this element i add a validator StringLength like this:
$this->addElement('textarea', 'text', array('label' => 'F_MESSAGE_SMS_TEXT', 'required' => true, 'class' => 'fieldLiveLimit', 'limit' => 160));
$this->text->addValidator('StringLength', false, array('max' => 160));
and i use a script javascript for show live countdown characters:
//Text field or text area limit - get limit by field parameter
$(".fieldLiveLimit").each(function () {
var characters = $(this).attr('limit');
var remaining = calcDifference(characters, $(this).val());
if ($('.limitCounter').length > 0) {
$(this).after($('.limitCounter').first().clone());
$(this).next('.limitCounter').children('span').html(remaining);
} else {
$(this).after($("<div class='limitCounter'>" + translate('L_LIMIT_COUNTER', [remaining]) + "</div>"));
}
checkClassCounter(remaining, $(this));
$(this).bind('textchange', function (event, previousText) {
remaining = calcDifference(characters, $(this).val());
checkClassCounter(remaining, $(this));
if ($(this).val().length > characters) {
$(this).val($(this).val().substr(0, characters));
} else {
$(this).next('.limitCounter').children('span').html(remaining);
}
});
function calcDifference(characters, value) {
return characters - parseInt(value.length);
}
function checkClassCounter(remaining, element) {
if (parseInt(element.val().length) == 0) {
element.next(".limitCounter").hide();
} else {
element.next(".limitCounter").show();
if (remaining <= 10) {
element.next(".limitCounter").addClass('red-message');
} else {
element.next(".limitCounter").removeClass('red-message');
}
}
}
});
this works well, except for one thing. If inside the text area there are the new lines, the validator zend the new line it counts as two characters, while my JS script as one.
who is wrong? I think the zend validator, but it seems really strange as a thing and then ask to you!

It has to do with the line breaks, as user Pankrates already pointed out in his comment.
In fact, this problem is a lot more complex than it seems at first, because it has at least two dimensions:
jQuery strips the carriage return character in the val() function: "At present, using .val() on textarea elements strips carriage return characters from the browser-reported value." jQuery documentation.
It is inconsistent along browsers how line breaks with \r\n are counted. See here or here for related questions on SO. However, on all browsers I have installed on my system (Firefox 20.0 and Chrome 26.0), \r\n are counted as two characters, so I cannot confirm this.
See this little code snippet for a demonstration:
<?php
$str1 = "test\nstring";
$str2 = "test\r\nstring";
?>
<textarea id="text1"><?php echo $str1 ?></textarea>jQuery: <span id="jquery1"></span>, JS: <span id="js1"></span>, PHP: <?php echo iconv_strlen($str1) ?>
<textarea id="text2"><?php echo $str2 ?></textarea>jQuery: <span id="jquery2"></span>, JS: <span id="js2"></span>, PHP: <?php echo iconv_strlen($str2) ?>
<script type="text/javascript">
$(document).ready(function() {
$("#jquery1").text($("#text1").val().length);
$("#js1").text("<?php echo str_replace(array("\n", "\r"), array('\n', '\r'), $str1) ?>".length);
$("#jquery2").text($("#text2").val().length);
$("#js2").text("<?php echo str_replace(array("\n", "\r"), array('\n', '\r'), $str2) ?>".length);
});
</script>
For the first box it gives me jQuery: 11, JS: 11, PHP: 11, but for the second box I get jQuery: 11, JS: 12, PHP: 12.
There are several solutions I can think of (none of which is ideal):
Use a Zend_Filter_PregReplace in your form to replace all \r\n with \n. Pro: Counting will be consistent with that of jQuery's val() and relatively easy. Con: You are destroying the user's line break which might lead to unwanted results.
Decorate the Zend_Validate_StringLength so that you can replace \r\n by \n in the isValid() method. Pro: Will preserve the user's line break, Con: You might get a valid result that is longer then 200 characters, because \r\n is counted as one character and you need to introduce a new class.
Use the jQuery's textarea.valHooks to replace all line breaks with \r\n: Pro: Simple, Con: If you have users that have \n as line break char, it again will give you inconsistent results.
I hope this answer show you some directions on how you can tackle this situation depending on your app's context.

Related

DOMXPath multiple contain selectors not working

I have the following XPath query that a kind user on SO helped me with:
$xpath->query(".//*[not(self::textarea or self::select or self::input) and contains(., '{{{')]/text()") as $node)
Its purpose is to replace certain placeholders with a value, and correctly catches occurences such as the below that should not be replaced:
<textarea id="testtextarea" name="testtextarea">{{{variable:test}}}</textarea>
And replaces correctly occurrences like this:
<div>{{{variable:test}}}</div>
Now I want to exclude elements that are of type <div> that contain the class name note-editable in that query, e.g., <div class="note-editable mayhaveanotherclasstoo">, in addition to textareas, selects or inputs.
I have tried:
$xpath->query(".//*[not(self::textarea or self::select or self::input) and not(contains(#class, 'note-editable')) and contains(., '{{{')]/text()") as $node)
and:
$xpath->query(".//*[not(self::textarea or self::select or self::input or contains(#class, 'note-editable')) and contains(., '{{{')]/text()") as $node)
I have followed the advice on some questions similar to this: PHP xpath contains class and does not contain class, and I do not get PHP errors, but the note-editable <div> tags are still having their placeholders replaced.
Any idea what's wrong with my attempted queries?
EDIT
Minimum reproducible DOM sample:
<div class="note-editing-area">
<textarea class="note-codable"></textarea>
<div class="note-editable panel-body" contenteditable="true" style="height: 350px;">{{{variable:system_url}}</div>
</div>
Code that does the replacement:
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($html);
$xpath = new DOMXpath($dom);
foreach ($xpath->query(".//*[not(self::textarea or self::select or self::input or self::div[contains(#class,'note-editable')]) and contains(., '{{{')]/text()") as $node) {
$node->nodeValue = preg_replace_callback('~{{{([^:]+):([^}]+)}}}~', function($m) use ($placeholders) {
return $placeholders[$m[1]][$m[2]] ?? '';
},
$node->nodeValue);
}
$html = $dom->saveHTML();
echo html_entity_decode($html);
Use this below xpath.
.//*[not(self::textarea or self::select or self::input or self::div[contains(#class,'note-editable')]) and contains(., '{{{')]

Overcoming "local ambiguity: multiple parsing options:" in Rust Macros

I was experimenting with Rust's macro_rules and wanted to make a macro which could parse an HTML like syntax and simply echo the HTML as a string. The below macro gets most of the way there:
macro_rules! html {
() => ("");
($text:tt) => {{
format!("{}", $text)
}};
(<$open:ident>[$($children:tt)*]</$close:ident>$($rest:tt)*) => {{
format!("<{}>{}</{}>{}",
stringify!($open),
html!($($children)*),
stringify!($close),
html!($($rest)*))
}};
}
and then to use the macro:
println!("{}",
html!(
<html>[
<head>[
<title>["Some Title"]</title>
]</head>
<body>[
<h1>["This is a header!"]</h1>
]</body>
]</html>
)
);
However, I would really like to remove the extraneous opening and closing square brackets. I attempt to do that as follows:
macro_rules! html_test {
() => ("");
($text:tt) => {{
format!("{}", $text)
}};
(<$open:ident>$($children:tt)*</$close:ident>$($rest:tt)*) => {{
format!("<{}>{}</{}>{}",
stringify!($open),
html!($($children)*),
stringify!($close),
html!($($rest)*))
}};
}
However, when I go to use this macro:
println!("{}",
html_test!(
<html>
<head>
<title>"Some Title"</title>
</head>
<body>
<h1>"This is a header!"</h1>
</body>
</html>
)
);
I get the error: local ambiguity: multiple parsing options: built-in NTs tt ('children') or 1 other option.
I know the general solution to this error is to add syntax to disambiguate the cases (such as adding the square brackets). Is there any other way around this issue for this specific example? I know using procedural macros would be an extreme solution, but I would prefer to use macro_rules if at all possible.
I realize using a macro to simply get a string containing HTML is overkill, but it was solely for the sake of this questions. Potentially, one could do much more interesting things with the macro such as calling functions to build up a tree representing the HTML structure.
Do you want the macro to actually be usable? Then no. Actually, why even use a macro here at all? No matter what you do, you're going to be fighting the Rust lexer at some point. Just write the HTML in a string literal like:
r##"<html>
<head>
<title>Some Title</title>
</head>
<body>
<h1>This is a header!</h1>
</body>
</html>"##
That or accept that macro input cannot match actual HTML syntax, close tab, move on.
You're still here? Oh, so you don't care about usability or performance? You really want a marginal improvement in syntax, no matter the cost? *rolls up sleeves*
Be careful what you wish for.
You need to use an incremental parser, which allows you to bypass some of the ambiguous parse issues. Rather than trying to match a non-delimited group (which you can't do), you instead recursively match unique prefixes. Doing that leads to:
macro_rules! html_test {
(#soup {$($parts:expr,)*}, [], ) => {
concat!($($parts),*)
};
(#soup $parts:tt, [$head:ident $($stack:ident)*], ) => {
compile_error!(
concat!(
"unexpected end of HTML; the following elements need closing: ",
stringify!($head),
$(",", stringify!($stack),)*
"."
)
)
};
(#soup {$($parts:tt)*}, [$ex_close:ident $($stack:ident)*], </$got_close:ident> $($tail:tt)*) => {
{
macro_rules! cmp {
($ex_close) => {
html_test!(
#soup
{$($parts)* "</", stringify!($ex_close), ">",},
[$($stack)*], $($tail)*
)
};
($got_close) => {
compile_error!(
concat!(
"closing element mismatch: expected `",
stringify!($ex_close),
"`, got `",
stringify!($got_close),
"`"
)
)
};
}
cmp!($got_close)
}
};
(#soup {$($parts:tt)*}, $stack:tt, <img $($tail:tt)*) => {
html_test!(#tag {$($parts)* "<img",}, $stack, $($tail)*)
};
(#soup {$($parts:tt)*}, [$($stack:ident)*], <$open:ident $($tail:tt)*) => {
html_test!(
#tag
{$($parts)* "<", stringify!($open),},
[$open $($stack)*],
$($tail)*
)
};
(#soup {$($parts:tt)*}, $stack:tt, $text:tt $($tail:tt)*) => {
html_test!(#soup {$($parts)* $text,}, $stack, $($tail)*)
};
(#tag {$($parts:tt)*}, $stack:tt, > $($tail:tt)*) => {
html_test!(#soup {$($parts)* ">",}, $stack, $($tail)*)
};
(#tag {$($parts:tt)*}, $stack:tt, $name:ident=$value:tt $($tail:tt)*) => {
html_test!(
#tag
{$($parts)* " ", stringify!($name), "=", stringify!($value),},
$stack, $($tail)*
)
};
($($tts:tt)*) => {
html_test! { #soup {}, [], $($tts)* }
};
}
This works by crawling over the input tokens, keeping track of the string pieces that need to be output (in $($parts)*), and the opened tags that need closing (in $($stack)*). Once it's out of input, and the stack is empty, it concat!s all the parts together, producing a single static string literal.
This has four problems:
This chews through recursion levels like crazy. If you run out, users will need to globally up the recursion limit.
Macros like this are slow.
Error reporting sucks. Although this will check the closing tags match the corresponding opening tags, problems aren't reported at any particular location in the invocation.
You still can't avoid needing to use string literals. You cannot match an expression that is followed by < or another expression, so matching the strings must be the (sole) fallback rule.
So you can remove the delimiters, but I wouldn't recommend it. Just quote the HTML like a sane person.
As an aside, here is an alternative version of the macro with a slightly different structure that factors out the cmp macro, and is easier to extend for elements without closing tags. Note that I did not write this version.

Find and extract content of division of certain class using DomXPath

I am trying to extract and save into PHP string (or array) the content of a certain section of a remote page. That particular section looks like:
<section class="intro">
<div class="container">
<h1>Student Club</h1>
<h2>Subtitle</h2>
<p>Lore ipsum paragraph.</p>
</div>
</section>
And since I can't narrow down using class container because there are several other sections of class "container" on the same page and because there is the only section of class "intro", I use the following code to find the right division:
$doc = new DOMDocument;
$doc->preserveWhiteSpace = FALSE;
#$doc->loadHTMLFile("https://www.remotesite.tld/remotepage.html");
$finder = new DomXPath($doc);
$intro = $finder->query("//*[contains(#class, 'intro')]");
And at this point, I'm hitting a problem - can't extract the content of $intro as PHP string.
Trying further the following code
foreach ($intro as $item) {
$string = $item->nodeValue;
echo $string;
}
gives only the text value, all the tags are stripped and I really need all those divs, h1 and h2 and p tags preserved for further manipulation needs.
Trying:
foreach ($intro->attributes as $attr) {
$name = $attr->nodeName;
$value = $attr->nodeValue;
echo $name;
echo $value;
}
is giving the error:
Notice: Undefined property: DOMNodeList::$attributes in
So how could I extract the full HTML code of the found DOM elements?
I knew I was so close... I just needed to do:
foreach ($intro as $item) {
$h1= $item->getElementsByTagName('h1');
$h2= $item->getElementsByTagName('h2');
$p= $item->getElementsByTagName('p');
}

PHP: If value is empty, to not sumbit target filepath to database

I have a very slight problem where I am not able to figure out how to get my target filepath not submit to the mysql database when the field value is empty. Right now, if I leave the image field empty, it still submits the filepath ($folder) to the database. I would like for when the field is left empty, to not send the filepath to mysql.
Form.php
<form enctype="multipart/form-data" action="add.php" method="POST">
HAZARD: <input name="haz1" value="hazard1" type="text" /><br>
<input type="hidden" name="MAX_FILE_SIZE" value="10000000" />
IMAGE: <input type="file" name="photo"><br>
<input type="submit" name="action" value="Load">
</form>
Add.php
<?php
$folder = "images/";
$target1 = $folder . basename( $_FILES['photo']['name']);
$photo = $target1;
require("../db.php");
$haz1 = $_POST['haz1'];
mysql_query("INSERT INTO testimg VALUES (null,'$haz1','$photo')") ;
move_uploaded_file($_FILES['photo']['tmp_name'], $target1);
?>
I've tried
if (isset($_POST['photo']) ? $_POST['photo'] : null) echo $target1 == null);
I've tried other ways of isset as well but doesn't seem to work. Is there any other way i can accomplish this? Appreciate any help please. Thank you!
(Just a note, I have removed excess code above just to keep it short. I am taking care of SQL injection)
I would strongly suggest JavaScript, then users do not need to reload the page if it is empty. The JavaScript will check if it is empty for you. If it is you can make it so they cannot submit at all.
function validateForm()
{
var x=document.forms["myForm"]["fname"].value;
if (x==null || x=="")
{
alert("First name must be filled out");
return false;
}
}
This is an example above, if you would like better walk through go here
JQuery has libraries that you can use to do fancy things if it is left blank, just search for JQuery form validation for more tools.
Hope this helps!
Try using the inbuild HTTP_POST_FILE in PHP:
if (isset($_FILES['photo']) ? $_FILES['photo'] : null)
You could wrap the mysql code inside of an if function too:
if (isset($_FILES['photo']) {
//Do mySQL processing in here
}
A couple of points:
Require is at the top of a PHP script. It's nicer to see all requires
first.
I have used an inline if statement to determine what to set $photo
(elimintating need for $target1)
I have also moved the apostrophes into the assignment of $photo as
returning 'null' comapred to null (without the quotation marks) is
very different in SQL.
If $photo is not null at the end of the script then it moves the
updated file.
Please see the corrected code below:
<?php
require("../db.php");
$folder = "images/";
$photo = (isset($_FILES['photo']) ? "'" . $folder . basename( $_FILES['photo']['name']) . "'" : null);
$haz1 = $_POST['haz1'];
mysql_query("INSERT INTO testimg VALUES (null,'$haz1',$photo)") ;
if ($photo != null) { move_uploaded_file($_FILES['photo']['tmp_name'], $photo); }
?>

how to make QR codes in perl cgi

I'm trying to create a website with forms for people to fill out and when the user presses submit button the texts in each form field are concatenated into a single text string to be used to make a QR code. How could I do this and what language would be the best for most browsers to be compatible.
In addition, I would like to have the text fields have a new line (\n) associated with it to make the format a little more pretty when the user scans the QR code.
Please let me know.. Thanks in advance.. could you include a sample code of a website that has three text areas to concatenate?
The Imager::QRCode module makes this easy. I just knocked the following up in 5 minutes.
#!/Users/quentin/perl5/perlbrew/perls/perl-5.14.2/bin/perl
use v5.12;
use CGI; # This is a quick demo. I recommend Plack/PSGI for production.
use Imager::QRCode;
my $q = CGI->new;
my $text = $q->param('text');
if (defined $text) {
my $qrcode = Imager::QRCode->new(
size => 5,
margin => 5,
version => 1,
level => 'M',
casesensitive => 1,
lightcolor => Imager::Color->new(255, 255, 255),
darkcolor => Imager::Color->new(0, 0, 0),
);
my $img = $qrcode->plot($text);
print $q->header('image/gif');
$img->write(fh => \*STDOUT, type => 'gif')
or die $img->errstr;
} else {
print $q->header('text/html');
print <<END_HTML;
<!DOCTYPE html>
<meta charset="utf-8">
<title>QR me</title>
<h1>QR me</h1>
<form>
<div>
<label>
What text should be in the QR code?
<textarea name="text"></textarea>
</label>
<input type="submit">
</div>
</form>
END_HTML
}
How could I do this and what language would be the best for most browsers to be compatible.
If it runs on the server then you just need to make sure the output is compatible across browsers; so use GIF or PNG.
could you include a sample code of a website that has three text areas to concatenate?
Just use a . to concatenate string variables in Perl.
my $img = $qrcode->plot($foo . $bar . $baz);
Add binmode to display the image of the qr code, for example:
print $q->header('image/png');
binmode STDOUT;
$img->write(fh => \*STDOUT, type => 'png');