Jump to content
photoman355

How to set text line/character limits in templates?

Recommended Posts

I'm sure the answer is fairly simple but I was wondering if there's an easy way to define the number of lines for a textfield in a template.  I know you can set size limits for images fairly easily using $image->size(100, 100); but wondered if there was the equivalent for text?  

Share this post


Link to post
Share on other sites

Assuming you mean the Admin side then it's nice and easy, just check the Input tab of the field in question and you'll see 'Rows' :)

My advice = DOH!

Edited by alanfluff

Share this post


Link to post
Share on other sites

Greeting photoman355,

This is not built into ProcessWire by default.  However, there are a couple of nice ways to add it.

I'm using a module in my sites that counts the entered characters, and disallows more than the set number of characters.

Here are the links:

Forum discussion: http://processwire.com/talk/topic/2343-char-counter-for-texttextarea-fields/

Module page: http://modules.processwire.com/modules/textarea-counter/

In the forum discussion, you will see both the module created by boundaryfunctions, and another concept by Soma.

One of these will work for you!

Thanks,

Matthew

Share this post


Link to post
Share on other sites

I think you mean something like this:

function wordLimiter($str, $limit = 120, $endstr = '…'){
    $str = strip_tags($str); 
    if(strlen($str) <= $limit) return $str;
        
    $out = substr($str, 0, $limit);
    $pos = strrpos($out, " ");
    if ($pos>0) {
        $out = substr($out, 0, $pos);
    }
    return $out .= $endstr;
}

echo wordLimiter($page->body);
  • Like 5
  • Thanks 1

Share this post


Link to post
Share on other sites

Hmm...not as simple as I thought.  

@alanfluff Thanks I knew about the admin side limits but the problem with adding a limit to a field is that you have to create a separate field for each size of text you need. So for example if I was reusing <?=$page->body?> multiple times the text limits would have to be the same.

@MatthewSchenker I had a read of the forum, really useful module.  In this case I can't use it as it uses the same way as Alan's method to set the limit. 

@Soma This is exactly what I was after, many thanks. I noticed you wrote this as a function.  I take it this is pulled from a functions.php file much in the same way as wordpress? To save writing lots of code this makes a lot of sense.  If I wanted to apply the function to say the $page->body tag would I use a get call first or is there a better way?

Share this post


Link to post
Share on other sites

Yes helper functions like this can be included using a separate php like in the head.inc and used throughout your templates.

I extracted this function from a module I have for a project. This module has various helper function and then I load it in the templates. It's much the same as if I would include a php with functions and just personal preference.

For example:

$helpers = $modules->get("TemplateHelpers");

then use it like this where I need it.

echo $helpers->wordLimiter($page->body);

I'm not sure what you mean by applying the function to the body. I use this function to create teaser texts that are limited, and show the complete body only on the detail page.

Of course you could modify the body output, that every time you do an echo $page->body, it will run it through a function, but I'm not sure this is a good practice.

This using a hook on the formatValue of textfields would do it: (directly in template like a include, or by making it a module)

function wordLimiter(HookEvent $event){
    $field = $event->argumentsByName('field');
    if($field->name != 'body') return;
    $str = $event->return;
    $limit = 150;
    $endstr = ' …';
    $str = strip_tags($str);
    if(strlen($str) <= $limit) return;
    $out = substr($str, 0, $limit);
    $pos = strrpos($out, " ");
    if ($pos>0) {
        $out = substr($out, 0, $pos);
    }
    return $event->return = $out .= $endstr;
}

wire()->addHookAfter("FieldtypeTextarea::formatValue", null, "wordLimiter");

// now this will trigger the above hook
echo $page->body;

But it's a little cumbersome, as you can't set the limit. Also this strips tags and on HTML text you'll lose formatting. But just to show adn example what is possible.

From your post I guess you like to do something like:

echo $page->body->limit(150); // not possible

It's not possible to do this, because the $page->body, body is just a string and not an object you could add methods to it.

But something like the following would be possible using hooks.

echo $page->wordLimiter("body", 120);

You can use addHook to add a method wordLimiter to page:

function wordLimiter(HookEvent $event){
    $field = $event->arguments[0]; // first argument
    $limit = $event->arguments[1];
    $endstr = isset($event->arguments[2]) ? $event->arguments[2] : ' …';
    $page = $event->object; // the page
    $str = $page->get($field);

    $str = strip_tags($str);
    if(strlen($str) <= $limit) return;
    $out = substr($str, 0, $limit);
    $pos = strrpos($out, " ");
    if ($pos>0) {
        $out = substr($out, 0, $pos);
    }
    return $event->return = $out .= $endstr;
}

// this will add a custom method to Page object
wire()->addHook("Page::wordLimiter", null, "wordLimiter");

// now you can do this
echo $page->wordLimiter("body", 100);

// or this
echo $page->wordLimiter("summary", 100);
  • Like 10

Share this post


Link to post
Share on other sites

Fantastic info Soma, lots of code to try out.  I really like the last method using hooks, seems like the closest I'll get to $page->body->limit(150).  Will have a play around.  Many thanks for all your help on this.

Share this post


Link to post
Share on other sites

Did I dream this or is something like this now part of the core in the dev branch?

Share this post


Link to post
Share on other sites

Did I dream this or is something like this now part of the core in the dev branch?

Not really....see Ryan's mini-tut here: http://www.flamingruby.com/blog/processwire-weekly-13/#1-2

You can easily do this using CKEditor's Wordcount plugin... ;)

Edit:

Misread first post. Disregard my post.

Edited by kongondo
Because I am an idiot :-)
  • Like 1

Share this post


Link to post
Share on other sites

Not really....see Ryan's mini-tut here: http://www.flamingruby.com/blog/processwire-weekly-13/#1-2

You can easily do this using CKEditor's Wordcount plugin... ;)

I think, the others are talking about limiting the output of a field. This plugin only counts words/chars inside of CKEditor.

Did I dream this or is something like this now part of the core in the dev branch?

@Pete: where did you found this..?

Share this post


Link to post
Share on other sites

I think, the others are talking about limiting the output of a field. This plugin only counts words/chars inside of CKEditor.

Aaah, thanks. Didn't read it properly, thanks. 

Share this post


Link to post
Share on other sites

@Soma Your code seems to do the work, but does have a little issue. If you use line break or have several paragraphs, it stacks the whole text into one big mess. I have a list in one of my news stories with names on each line. This way it messes up the content quite well :/ Any workarounds for this?

Share this post


Link to post
Share on other sites

You should strip or rip the tags before using the output with a text formatter or direct in the template...

/**
 * Wordlimiter cuts a textarea only after complete words not between
 * used in admin.php for seo function and in some templates
 */
function wordLimiter($str = '', $limit = 120, $endstr = '...'){
	if($str == '') return '';
	if(strlen($str) <= $limit) return $str;
	$out = substr($str, 0, $limit);
	$pos = strrpos($out, " ");
	if ($pos>0) {
		$out = substr($out, 0, $pos);
	}
	$out .= $endstr;
	return $out;
}

/**
 * Alternative with regex for striptags function
 * used in admin.php for seo function and in some templates
 */
function ripTags($string) {
    // ----- remove HTML TAGs -----
    $string = preg_replace ('/<[^>]*>/', ' ', $string);
    // ----- remove control characters -----
    $string = str_replace("\r", '', $string);    // --- replace with empty space
    $string = str_replace("\n", ' ', $string);   // --- replace with space
    $string = str_replace("\t", ' ', $string);   // --- replace with space
    // ----- remove multiple spaces -----
    $string = trim(preg_replace('/ {2,}/', ' ', $string));
    return $string;
}

//usage in template like
$default_desc = ripTags($page->shorttext);
$default_desc_out = wordLimiter($default_desc, 160);
//or short
$default_desc = wordLimiter(ripTags($page->shorttext), 160));

regards mr-fan

Share this post


Link to post
Share on other sites

@mr-fan, I guess I'm doing something wrong here:

$default_desc = wordLimiter(ripTags($page->body), 160));

I'm trying to output the body elements of several childpages. Something I'm missing here? I got all the other code in the page too, and it runs through, yet no output.

Share this post


Link to post
Share on other sites

@mr-fan Soma's solution does already strip tags. 

@Olli like LostKobrakai wrote somas code is complete and use striptags PHP function.

Please provide more information. Have you debug mode on? Have you tried it with only wordLimiter($page->body, 160); ? and/or only ripTags($page->body); ?

regards mr-fan

Share this post


Link to post
Share on other sites

@Olli I am doing the same (with the code from post above #6)

I get my child pages

$posts = $page->children;

I loop through the posts:

foreach($posts as $p){
  $pbody = $p->wordLimiter("body", 100);
  echo($pbody);
}

& this produces the desired output.

Share this post


Link to post
Share on other sites
On 11.1.2016 at 12:08 PM, Olli said:

@Soma Your code seems to do the work, but does have a little issue. If you use line break or have several paragraphs, it stacks the whole text into one big mess. I have a list in one of my news stories with names on each line. This way it messes up the content quite well :/ Any workarounds for this?

I have searched for a solution. I would shorten the text from my blog entries. This seems to work for me. It keeps the html tags from the editor:

function truncateHtml($text, $length = 100) {
    $current_size = strlen($text);
    $diff = strlen($text);
    $remainder = $current_size - $length;
    while($diff > 0 AND $remainder > 0) {
        $pattern = "/(.*)[^<>](?=<)/s";
        $text = preg_replace($pattern, "$1", $text);
        $diff = $current_size - strlen($text);
        $current_size = strlen($text);
        $remainder = $current_size - $length;
    }
// iff $diff == 0 there are no more characters to remove
// iff $remainder == 0 there should removed no more characters
    return $text;
}

It's from here: https://stackoverflow.com/questions/38548358/cut-html-input-while-preserving-tags-with-php

Post Number 4.

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By sambadave
      Hi there
      Short version of question
      Let's say I have a page in the admin that contains a field... Is it possible to output the content from that field on another page in the admin? Almost like a reference.
      Longer version of question (with example)
      A house builder with multiple (60+) developments. They want to be able to create notices/messages that can be added to one or many developments. Handy for things like regional covid lockdowns or temporary office closures due to bad weather.
      My approach for the admin editing options:
      Add each message to each development
      Pros: You edit the message on the development page in context
      Cons: Very time consuming and repetitive if the same message needs to be applied to 60+ developments
        Control all the messages from one admin page and say which development each message should be applied to
      Pros: Easier to add/remove messages to more than one development at a time. Control all messages from one place.
      Cons: Content is not added on development page, which is where typical admin users may expect to find it I went for option 2 due to flexibility, and created a page within the admin for global development notices. This contains a repeater with:
      Field for message to display Checkbox list of all developments. The user can select which ones to apply each message to It's working really well but the only thing is that if the user goes to a specific development in the admin, the relevant messages aren't displayed in context (as they aren't edited on that page and instead on the global development notices page)... which may cause confusion when a new staff member / content admin tries to edit the text but there is no field when they go to the development admin page where they expect to see it...
      Solution???
      I don't require the message(s) to also be editable on the development page, but I wondered if there was a nice way to show it/them somehow. I feel like I am missing something really simple as I'm sure ProcessWire will have a nice way of achieving this, or maybe there are field settings that allow this kind of thing to happen?
      Any ideas on approaches or similar experiences would be much appreciated, even if it is just a much simpler example with the content from one field being shown on another admin page to get the ball rolling.
      Thanks in advance for any advice :)
    • By daniel_puehringer
      Hi community, thanks for the great work you do 🙂

      I´m looking for a global field which can be used in the footer and has only one instance for all pages. So I´m kind of looking for "One field to rule them all".
       
      Reason for my question: The website I´m currently building has one footer for all pages. The content of the footer should be dynamic - therefore I made a global field which is automatically inserted into each page.
      Problem: if I want to change something in the footer, I have to do it for all fields, because each field has it´s own instance.

      Question: is it possible to have one global field in which changes the content for the footer of all pages?

      In most CMS Systems a simple solution to this is often impossible, I hope that processwire is different 🙂

      All the best,
      Daniel
       
    • By Pip
      Hi, Everyone! 
      I'm currently working on a page reference field and set it for multiple pages (AsmSelect) for the input. Is there a way for me to add an image field (aka Avatar) and the title of page in the radio button? 
      I used the field name enclosed in the { }. Didn't work. It appeared a text instead. 
      Thanks in advance and hope to hear from you soon!
    • By EyeDentify
      Hello dear PW gurus.

      I have stumbled over a strange error that i all of sudden got when trying to upload an image to a images field on a page.
      There where images allready stored in the field that i wanted to keep, but during the upload the error apear and after that all images are gone from the field and i can´t upload any, i just get the error every time.
      I am running ProcessWire 3.0.153 dev.
      Update:
      After looking in the assets folder i find the folder for the page and the image files seems to be there including the ones i tried to upload when the error occured.
      But they don´t show up in the images field in the page editor.
       
      The error reported:
       
      SQLSTATE[01000]: Warning: 1265 Data truncated for column 'ratio' at row 1 And here is a screenshot of the event:

      The TracyDebugger Error reporting:

      I hope you fine folks could point me in a direction.
      But it seems our old pal set_time_limit() is back.

      Regards, EyeDentify
    • By prestoav
      Hi all and thanks for the great work on PW!

      One thing I find I have to do on any new site is add the Justify plugin to textarea fields as it is such a widely required feature for text headings in content.

      ANy chance this could be added to the core an automatically be installed on new textarea fields using CKEditor?

      Thank you!
×
×
  • Create New...