bytesource Posted March 28, 2014 Share Posted March 28, 2014 Hi,I want to remove the surrounding paragraph tags, <p> and </p> from the output of the Textile text formatter, but only for associated fields of type text.The reason for this is that the additional paragraph tags often interfere with the page markup, e.g. when output inside header tags like this: //Text: "Hello World!" <h1><?php echo $page->fieldUsingTextile; ?></h1> // Output: // <h1><p>Hello World!</p><h1> The module code is short and easy to follow: public function format(&$str) { require_once($this->config->paths->TextformatterTextile . 'classTextile.php'); $textile = new Textile(); $str = $textile->TextileThis($str); } In order to strip out the surrounding paragraph tag, I added the following line at the bottom of the function: // Remove surrounding paragraph tags $str = preg_replace('/<p>(.*)<\/p>/', '$1', $str); The problem is that his code strips the outer paragraph tags for fields of type text and textfield. However, removing the outer paragraph tags on a possible multi-paragraph textfield does not make much sense and invalidates the HTML.So I need a way to say something like this: if (getFieldtype == 'Text' || getFieldtype == 'TextLanguage') { // remove paragraph tags as shown above } Any ideas are highly appreciated!Cheers,Stefan Link to comment Share on other sites More sharing options...
adrian Posted March 28, 2014 Share Posted March 28, 2014 All you need is: if($fields->get("fieldUsingTextile")->type == "FieldtypeText"){ I haven't played with textile, but I am curious why a plain text field would have paragraph tags in the first place. Are they coming from textile, or are they being pasted in and not stripped, as they would be in a normal plain text field? Link to comment Share on other sites More sharing options...
bytesource Posted March 28, 2014 Author Share Posted March 28, 2014 @adiran Thanks for your reply. To be honest, I don't quite understand the code you suggested. The selector "fieldUsingTextile" suggests I need to know the name of the field in advance, but what I want to accomplish is that whatever field calls the format function of this module (here is the complete code on Github) will get checked for its type. If the field type is FieldtypeText or TextLanguage, the surrounding paragraph tags are stripped, otherwise no further transformation is applied. All the Textile module does is calling the (official, I guess) Textile class, so this is probably where the additional paragraph tags are coming from. Cheers, Stefan Link to comment Share on other sites More sharing options...
diogo Posted March 28, 2014 Share Posted March 28, 2014 You can create a new text formater that does only that instead of modifying textile. Than you only have to apply it after textile on the fields where you need it. Link to comment Share on other sites More sharing options...
bytesource Posted March 28, 2014 Author Share Posted March 28, 2014 @diogo Thanks for your reply. Acually that is what I have been doing. I am using the Textile module on textfield fields and my own module on text fields. I just wanted to be able to make the selection dynamically, instead of having to manually choose one of the formatters whenever I create a new text/textfield field type. Also, I'd like to better understand inner workings of Processwire. So I am also asking this question out of curiosity. Cheers, Stefan Link to comment Share on other sites More sharing options...
diogo Posted March 28, 2014 Share Posted March 28, 2014 Ok, there is $field->textformatters that retrieves an array of all textformatters, you can use it for instance like this: if (in_array('TextformatterTextile', $field->textformatters)) //do something 2 Link to comment Share on other sites More sharing options...
bytesource Posted March 28, 2014 Author Share Posted March 28, 2014 @diogo What I really want to know the type of the field that calls the Textile textformatter. Do you know how I could accomplish this inside a module function? Cheers, Stefan Link to comment Share on other sites More sharing options...
Soma Posted March 28, 2014 Share Posted March 28, 2014 Textile Textformatter still uses deprecated syntax public function format(&$str) { New is public function formatValue(Page $page, Field $field, &$value) { So you can't hook into and check for the field Just add a second textformatter already in core "TextformatterPstripper" (wich uses the old syntax too btw) after the textile formatter on your text field. Easy. 3 Link to comment Share on other sites More sharing options...
Soma Posted March 28, 2014 Share Posted March 28, 2014 If a textformatter uses fomatValue you could hook into a get the field $this->hookAfter("TextformatterTextile::formatValue", $this, "hookTextile"); ... public function hookTextile($event){ $field = $event->arguments("field"); $value = $event->arguments("value"); if($field->type == "FieldtypeText") $value = str_replace(array("<p>","</p>"), "", $value); } 3 Link to comment Share on other sites More sharing options...
bytesource Posted March 28, 2014 Author Share Posted March 28, 2014 @soma Thanks for your great advice! I guess I could also just copy the Textile code into my own module, replace the format function with formatValue and then retrieve the type of the field with $field->type. It's a bit late already, but I'll try tomorrow. Cheers, Stefan Link to comment Share on other sites More sharing options...
Soma Posted March 28, 2014 Share Posted March 28, 2014 No need to, read again my post: There's a paragraph stripper already coming with PW: https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/modules/Textformatter/TextformatterPstripper.module$$ Just add it to the text field after Textile formatter. Link to comment Share on other sites More sharing options...
bytesource Posted March 28, 2014 Author Share Posted March 28, 2014 @soma I read your post I also read the code of TextformatterPstripper, and does indeed what I need. However, I wanted to be able to always just select one formatter for both text fields and textfield fields, and then remove the surrounding paragraph tags dynamically, based on the type of the field inside the module. I know, this might be a bit overkill for such a small problem, but at least I was forced to find a way to retrieve the type of a field. When I could not find a solution, I asked here in the forum. Thanks again for your suggestions. They really helped me a lot! Cheers, Stefan Link to comment Share on other sites More sharing options...
netcarver Posted March 29, 2014 Share Posted March 29, 2014 @bytesourceI'm one of the maintainers of Textile and I'm curious about your use of it for things like header fields (for which textile already has a syntax; 'h1.') Are you mainly trying to access Textile's typographic features such as automatic curly quotes etc in your fields? Do your fields ever hold more than a paragraph's worth of text?If you only need typographic features for single lines, then we really have a new use-case for Textile which has (to date) been focused on transformation of documents rather than just fields and hence why it treats your single line fields as paragraphs by default.I'm overdue updating the Textile textFormatter and hope you can get back to me soon as it may be possible to get this sorted out and included in the update should it prove relatively simple. 1 Link to comment Share on other sites More sharing options...
bytesource Posted March 29, 2014 Author Share Posted March 29, 2014 @netcarver I use the Textile module on both fields of type text and textfield: textfield: Multi-line/multi-paragraph. The output is (and should be) at least one paragraph, so no problem here with the paragraph tags. text: Single line, mainly used for headers (h1-h5). Enclosing the output in a paragraph interferes with the styling of the site. I know that I could just, e.g. for a h2 heading, write the text as h2. This is my main header But if I later wanted to change h2 to h3, I had to go through all occurrences of this field in the admin and carry out the change. That's why I would prefer Textile to not wrap the output of a (single line) text field into paragraph tags. This would allow me to write code like this: // some template file // field type: text // field name: header // content: Hello World! <h2><?php echo $page->header; ?></h2> // => <h2>Hello World!</h2> In case I later wanted to change h2 to h3, I only needed to make one small change in the template file. Cheers, Stefan Link to comment Share on other sites More sharing options...
netcarver Posted March 29, 2014 Share Posted March 29, 2014 Hi Stefan and thanks for the reply. Understood about the change of heading level - but part of my original post remains: are you using Textile on these single line fields purely for the typography changes? Link to comment Share on other sites More sharing options...
bytesource Posted March 30, 2014 Author Share Posted March 30, 2014 Hi Steve, As for me, typographic changes on single line text fields (Text and TextLanguage) mostly comprise of making a word bold or cursive. Sometimes I also include a <span> tag (%some word%) that holds a CSS class, to achieve a more custom styling. I have found that I never need a single line text to be wrapped in paragraph tags. However, I am not a web designer, so my use case might be a little bit unusual. Therefore I'd love to hear the opinion on this topic from someone more experienced than me. Cheers, Stefan Link to comment Share on other sites More sharing options...
netcarver Posted March 30, 2014 Share Posted March 30, 2014 Thanks Stefan, That gives me a better idea of what you are doing with these fields. I'll see what can be done to accommodate something for single fields in the next release. Link to comment Share on other sites More sharing options...
apeisa Posted March 30, 2014 Share Posted March 30, 2014 Not following whole discussion, but just mentioning that you can combine multiple textformatters for single field. Like textile + paragraph stripper. Link to comment Share on other sites More sharing options...
Soma Posted March 30, 2014 Share Posted March 30, 2014 Not following whole discussion, but just mentioning that you can combine multiple textformatters for single field. Like textile + paragraph stripper. That's what I was trying to communicate, but no luck. It's what I do in a similar situation and it's the perfect solution to this problem. The paragraph stripper is exactly built for this. But why simple if you can make it complicated. Link to comment Share on other sites More sharing options...
bytesource Posted March 31, 2014 Author Share Posted March 31, 2014 @Soma As in my case a single text field will never be wrapped in a paragraph, I like the idea of handling this case dynamically, instead of adding a second formatter to every field of type text. Thanks to your help this actually was not that complicated to accomplish. First I 'updated' the Textile module to use formatValue() instead of format(): public function formatValue($page, $field, &$value) { require_once($this->config->paths->TextformatterTextile . 'classTextile.php'); $textile = new Textile(); $value = $textile->TextileThis($value); } As formatValue() is hookable, I added the following hook to _init.php: templates/_init.php wire()->addHookAfter("TextformatterTextile::formatValue", function(HookEvent $event) { $field = $event->arguments("field"); $value = $event->arguments("value"); if($field->type == "Text" || $field->type == "TextLanguage") { // Remove paragraph tags on single line text fields. $value = str_replace(array("<p>","</p>"), "", $value); } }); There is one problem left, though. The after hook apparently does not get called. For example, placing an echo statement somewhere inside the function body does not yield any output. That's why I would be glad if you could point me in the right direction, even though you might prefer another solution to this formatting problem. Cheers, Stefan Link to comment Share on other sites More sharing options...
Soma Posted March 31, 2014 Share Posted March 31, 2014 As in my case a single text field will never be wrapped in a paragraph, I like the idea of handling this case dynamically, instead of adding a second formatter to every field of type text. I don't really get the reason why not? There must be a reason why you restrain from adding the second Textformatter that does exactly what you want? foreach($fields->find("type=FieldtypeText") as $f) { $f->textformatters = array("TextformatterTextile", "TextformatterPstripper"); // add textformatters $f->save(); } Done. Not sure why your hook isn't working. You mentioned that you modified the textile formatter, how and where? Make sure it is really called TextformatterTextile. 1 Link to comment Share on other sites More sharing options...
bytesource Posted March 31, 2014 Author Share Posted March 31, 2014 @Soma Please don't get me wrong. Your solution is quite elegantt, and I probably will pick it over the hook approach. I extended the selector to make the function work on all text fields: foreach($fields->find("type=FieldtypeText|FieldtypeTextLanguage") as $f) { $f->textformatters = array("TextformatterTextile", "TextformatterPstripper"); // add textformatters $f->save(); } The only reason I choose to use a hook is because I wanted to become more conformable with this technique. As for the Textile module, I replaced the current format() function (code on Github) with formatValue(): public function formatValue($page, $field, &$value) { require_once($this->config->paths->TextformatterTextile . 'classTextile.php'); $textile = new Textile(); $value = $textile->TextileThis($value); } I am not sure why the after hook I added does not get called. The formatValue() function does indeed exist and the name of the class is TextformatterTextile. Cheers, Stefan Link to comment Share on other sites More sharing options...
Soma Posted March 31, 2014 Share Posted March 31, 2014 I'm not against at all with you doing it with a hook, just am confused why you choose the "extra" manual hook way over what is obviously already there and can be done via adding multiple textformatters. It's exactly designed for what you need here, so have hard time seeing the need to do extra workaround with a hook. But if it's for learning reason, why not... I'm not sure what you mean my solution is elegant? The code to add the Textformatters? Or simply using a second textformatter on the text field, which is obviously not my solution but designed best practice in PW. I think the method you have isn't hookable because it's not hookable. Does it have like "___formatValue" three underscores? Link to comment Share on other sites More sharing options...
Soma Posted March 31, 2014 Share Posted March 31, 2014 Ahm, so just missed that also with my earlier posts, it's that formatValue() or format() in Textformatters arent really hookable as it stands now. What you would instead do is hook into the text fields and hook into formatValue(), which is where textformatters are run when output formatting is on. Those are hookable in the system as they contain ___. So it would look like this instead of hooking a textformatter: $this->addHookAfter("FieldtypeText::formatValue", $this, "hookTextFormating"); public function hookTextFormatting($event){ $field = $event->arguments("field"); $value = $event->arguments("value"); .... } Or in a template wire()->addHookAfter("FieldtypeText::formatValue", null, "hookTextFormating"); function hookTextFormating($event){ $field = $event->arguments("field"); $value = $event->arguments("value"); .... } So after all coming this route, it doesn't matter if a Textformatter uses format() or formatValue(). 1 Link to comment Share on other sites More sharing options...
bytesource Posted April 1, 2014 Author Share Posted April 1, 2014 @Soma I find your solution elegant - the one adding a second texformatter - because it accomplishes everything in a few lines of code. Also, I am thankful for every line of code that allows me to better understand the design of Processwire. As for my method hooking into TextformatterTextile::formatValue, I assumed that this was the one that was hookable. Thanks to your remark and Captain Hook's confirmation, I now know I should have hooked into FieldtypeText::formatValue. So this is the code I came up with: wire()->addHookAfter("FieldtypeTextLanguage::formatValue", function(HookEvent $event) { $value = $event->arguments("value"); // Remove paragraph tags on single line text fields. $value = str_replace(array("<p>","</p>"), "", $value); $event->return = $value; }); Unfortunately, I still don't get the intended output. What is returned is the completely unformatted text (including all Textile specific identifiers). EDIT: Returning the unformatted string indicates the hook is called before application of the Textile textformatter. Is there any way to make sure my hook gets called on the output of the Textile textformatter? Cheers, Stefan Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now