Jump to content

Module: RuntimeMarkup Fieldtype & Inputfield


kongondo

Recommended Posts

Thanks @adrian

@Robin S's module is simple and sweet. Sadly cannot use in FormBuilder as it has both the Inputfield class and the Fieldtype class within the FieldtypeRuntimeOnly.module file. FormBuilder looks for module files prefixed with 'Inputfield' in its list of approved input types.

I use @bernhard's module 'RockMarkup2' elsewhere in the project and only discovered that it's designed to only work in admin and it's been deprecated. However, just tried it in FormBuilder and it works a treat.

Maybe Robin and Bernhard can get together to come up with an easy to use module that has separate files for Fieldtype & Inputfield. That would be awesome! 

Link to comment
Share on other sites

Hey @psy I'd create a custom runtime module, it's not too hard:

1) Create the fieldtype:

2) Create the inputfield:

RockMarkup is deprecated because I don't plan to use it as base module for other Fieldtypes in the future. I think it makes things more complicated and that's not worth it. Building custom fieldtypes + inputfields is easy once you know how things work. Especially runtime fields are easy because you don't need all the DB related parts.

If you need any help just ask.

  • Like 2
Link to comment
Share on other sites

3 minutes ago, bernhard said:

I'd create a custom runtime module

Thanks @bernhard, after looking at your RockMarkup2 and Adrian's RuntimeOnly modules + your tutorials, am sure I could figure it out. For this project however, I'm under the gun with a rapidly approaching deadline. Will explore creating my own module when the pressure is off.

Link to comment
Share on other sites

@psy, in theory you don't need any sort of special module to put runtime markup into a field. Instead just add a Markup field to the form and then hook before the form is rendered to put whatever markup you want into it.

Having said that, there are some oddities with Markup fields in FormBuilder. I raised some questions that Ryan answered here: 

 

But I still had some other problems (I can't remember exactly what they were now) that I ended up resolving by making a clone of the core InputfieldMarkup module with some minor changes. I've attached the resulting InputfieldCustomMarkup module. If you install this module and enable it for use in FormBuilder then you can use it the same as a Markup field but without the snags.

InputfieldCustomMarkup.zip

  • Like 2
Link to comment
Share on other sites

  • 1 month later...

Great module, but I'm having a couple of minor issues (unrelated, I think).

The first issue seems intermittent and I can't work out why In some cases, I can't get the inclusion of .js and .css to work. I've triple-checked all the naming etc. but the files just don't seem to load. My work-round (easy enough) is just to load the files in the php.

The second issue may be because I am stretching the fieldtype's capability! I am using it within a PageTable field as follows.

The PageTable field ("panelled_carousel") has a template "Panelled_carousel" which has fields "title", "imagePages" (a page reference field linking to a page with template "Image" and an image field "image_single") and "runtime_markup_images" (a runtime markup field with php file "RuntimeMarkup/Images.php").

The php file is (including a bit of debugging):

$imagePages = $page->imagePages;
bd($page, 'page with carousel');
$out = '';
wire()->log->save('debug', 'Page is ' . $page->title);
foreach ($imagePages as $imagePage) {
    bd($imagePage, 'imagePage');
    foreach ($imagePage->image_single as $image) {
        bd($image, 'image');
       if (is_a($image, '\ProcessWire\Pageimage')) $out .= '<img src="' . $image->pia("width=200")->url . '"/>';
    }
}
return  $out;

The RM field is updated when the imagePages field changes by javascript/AJAX :

When editing the Page Table field, this all works well - any changes in the imagePages (add/re-order/delete etc) are immediately reflected in the RM field. However, when saving the Page Table Field and returning to the host page, the RM PHP throws an error unless I catch it with the condition - is_a($image, '\ProcessWire\Pageimage') - in the code above. The reason is that in some cases $image seems to be taking a string, integer or boolean value, even though $imagePage has class Page and the dump shows it is the right page. Trapping this error means that the images in the table are all blank after the save. However refreshing the page puts everything right again. So not a deal-breaker but a bit odd.

To illustrate, here is the top page with the page table (including images):

1403291032_Toppage.thumb.jpg.ba0e32c4ea399931384b1e698af6fcad.jpg

Clicking on "Blue" to edit an item we get:

837229460_EditPageTableitem.thumb.jpg.41ff6cfec4e5f679cb7e63155d4e99f2.jpg

Then, clicking save and returning to the top page, the images do not render:

431478913_Aftersave.thumb.jpg.90ed19714a18bf1d45f8a1fa99e14c93.jpg

The debugging shows that this is not because Images.php has not run, but because $image is of the wrong type (as described above).

Refreshing the page restores the images.

Any ideas?

EDIT: I should add that the problem does not seem to be related to my .js script, or even RuntimeMarkup specifically as exactly the same issue happens in a Page Table with @Robin S's RuntimeOnly module with no js attached to it. So it seems to be something to do with how the runtime markup is interacting with the page table display.

Link to comment
Share on other sites

To make this work inside a page table, I had to hack the InputfieldPageTable.js as follows:

function InputfieldPageTableDialog() {

	var $a = $(this);
	var url = $a.attr('data-url');
	var title = $a.attr('data-title');
	var closeOnSave = true;
	var $container = $(this).parents('.InputfieldPageTableContainer');
	var wrap = $(this).closest('.InputfieldPageTable'); // MDE added
	var dialogPageID = 0;
	var noclose = parseInt($container.attr('data-noclose'));
	var modalSettings = {
		close: function(event, ui) {
			if(dialogPageID > 0) {
				var ajaxURL = $container.attr('data-url') + '&InputfieldPageTableAdd=' + dialogPageID;
				var sort = $container.siblings(".InputfieldPageTableSort").val();
				if(typeof sort != "undefined" && sort.length) ajaxURL += '&InputfieldPageTableSort=' + sort.replace(/\|/g, ',');
				$.get(ajaxURL, function(data) {
					$container.html(data);
					$container.find(".Inputfield").trigger('reloaded', ['InputfieldPageTable']);
					$container.effect('highlight', 500, function() {
						var $table = $container.find('table');
						$table.find('tbody').css('overflow', 'visible');
						InputfieldPageTableSortable($table);

						// restore appearnace of any items marked for deletion
						var deleteIDs = $container.siblings("input.InputfieldPageTableDelete").eq(0).val().split('|');
						if(deleteIDs.length) {
							for(var n = 0; n < deleteIDs.length; n++) {
								var deleteID = deleteIDs[n];
								$table.find("tr[data-id=" + deleteID + "]")
									.addClass('InputfieldPageTableDelete ui-state-error-text ui-state-disabled');
							}
						}
					});
				});
				if ($(wrap).attr('id')) $('#' + wrap.attr('id')).load(document.URL +  ' #' + wrap.attr('id')); // MDE added
			}
		}
	}
                                                                ....

The new lines are the ones annotated "MDE added"

I haven't found any unwanted side-effects (so far ..) but obviously I'm a bit unhappy about amending core code unless it warrants a PR, so I would be grateful for any thoughts on this, or other ideas.

Link to comment
Share on other sites

11 hours ago, MarkE said:

The debugging shows that this is not because Images.php has not run, but because $image is of the wrong type (as described above).

Refreshing the page restores the images.

Any ideas?

Sorry Mark, I got lost in that Green Bedroom. Man, I am dog tired and could do with that rest! 😄 

OK, back to reality. I haven't used the Page Table field in ages. How does it save stuff? Via Ajax or normal POST? I am just wondering whether this has to do with renderReady() as I have had similar issues before with Media Manager and Repeaters. Currently,  I don't have time to test but hopefully can get to this soon.

I've just seen your edit: Ajax it is.

Edited by kongondo
Link to comment
Share on other sites

Putting this:

    $(document).on('pw-modal-closed', function(event) {
        var wrap = $(this).find('.InputfieldPageTable');
        if ($(wrap).attr('id')) $('#' + wrap.attr('id')).load(document.URL +  ' #' + wrap.attr('id'));
    });

in the admin js avoids the need to hack the InputfieldPageTable.js. Again, there may be side effects that I haven't noticed. One side-effect I have noticed is that, after closing the modal, the rows in the Page Table can no longer be sorted by dragging - a full window refresh is still required for that.

Link to comment
Share on other sites

Here's my complete work-round for the problem with runtime markup images in a Page Table field. Something that prevents the problem occurring in the first instance would be better though!

1. Add the following condition for images rendered in the runtime markup:

if (is_a($image, '\ProcessWire\Pageimage')) $out .= '<img src="' . $image->url . '"/>';

2. Add the following to admin.js (the 2 functions are lifted from InputfieldPageTable.js):

/*******************************************************************************************************************/
/* Work-round to reload and re-enable sorting of Page Table field, which includes runtime markup, after modal edit */
/*******************************************************************************************************************/
    $(document).on('pw-modal-closed', function(event) {
        var wrap = $(this).find('.InputfieldPageTable');
        if ($(wrap).attr('id')) $('#' + wrap.attr('id')).load(document.URL +  ' #' + wrap.attr('id') + '> *'); // To just load the inner html of wrap - avoiding duplicating it
    });

    $('.InputfieldPageTable').mouseenter(function () {
        if (!$('tbody').hasClass("ui-sortable")) {
            var $table = $('tbody').closest('table');
            InputfieldPageTableSortable($table);
        }
    });

    function InputfieldPageTableUpdate($table) {
        var value = '';
        if(!$table.is('tbody')) $table = $table.find('tbody');
        $table.find('tr').each(function() {
            var pageID = $(this).attr('data-id');
            if(value.length > 0) value += '|';
            value += pageID;
        });
        var $container = $table.parents('.InputfieldPageTableContainer');
        var $input = $container.siblings('.InputfieldPageTableSort');
        $input.val(value);
    }

    function InputfieldPageTableSortable($table) {

        $table.find('tbody').sortable({
            axis: 'y',
            start: function(event, ui) {
                var widths = [];
                var n = 0;
                $table.find('thead').find('th').each(function() {
                    widths[n] = $(this).width();
                    n++;
                });
                n = 0;
                ui.helper.find('td').each(function() {
                    $(this).attr('width', widths[n]);
                    n++;
                });
            },
            stop: function(event, ui) {
                InputfieldPageTableUpdate($(this));
            }
        });

    }

/*******************************************************************************************************************/

 

Link to comment
Share on other sites

12 hours ago, kongondo said:

Still not sure whether this is a RM issue or a PageTable one.

Nor me. It seems like PT calls the RM PHP file more than once. The first time it works properly with a Pageimage, but then later calls submit a string/integer/boolean. The code to bypass the latter has the side effect that the RM field now has nothing in it - hence the need for the reload. All pretty messy! I've tried various debugging routes, but can't work out how the multiple calls are happening.

Link to comment
Share on other sites

Hi Kongondo,

 

first of all many thanks for this useful module!

My question is about the behaviour of RM fields on save. Usually, we fill the RM fields with some static html markup. After saving pages with RM fields, these fields always are anounced as "changed", but why? Is is possible to prevent this?

 

Best regards,

Thomas.

  • Like 1
Link to comment
Share on other sites

Hi @xportde. I have noticed the same thing. I assume this is important to you because you have some hook or other that you only want to operate if fields have changed. What I do is simply catch and exclude any field changes that I'm not interested in. E.g. in a Pages::saved hook:

        $excludeFields = ['runtime_markup_parent', etc.......];
        $changes = array_diff($event->arguments(1), $excludeFields);

 

Link to comment
Share on other sites

6 hours ago, xportde said:

After saving pages with RM fields, these fields always are anounced as "changed", but why? I

Because I tend to be a silly billy often times 😄.

It's happening because RM is not letting its parent class know that it is handling the processing of its WireInputData

6 hours ago, xportde said:

Is is possible to prevent this?

Yes, it is. I'll make the changes in the repo eventually, but meanwhile....please open up InputfieldRuntimeMarkup.module for editing. Scroll down to the end of the file. Find the method ___processInput() and un-comment it. The method's body is intentionally blank, so no changes needed there. Save :-). That should do it.

  • Like 1
Link to comment
Share on other sites

  • 2 months later...

Hi,

I tried your module but in the end went for @Robin S 's one. However when trying to uninstall the inputfield first, I had an error saying the class "RuntimeMarkupUtilities" did not exist. I just cut-pasted it from the fieldtype to the inputfield and all went good after that.

I don't know if it changes anything when using the module, but it might be worth to update the code to ease the uninstall process.

Link to comment
Share on other sites

2 minutes ago, monollonom said:

However when trying to uninstall the inputfield first, I had an error saying the class "RuntimeMarkupUtilities" did not exist.

Yes, it's a known issue that I've never gotten round to fixing (although several fixes exist, a number of posts in this thread). I'll get to it at some point. Sorry for the pain.

Link to comment
Share on other sites

  • 1 month later...
On 11/11/2020 at 9:08 AM, Orkun said:

I am using this field also inside the lister.

I can't remember if I ever tested this inside Lister but I doubt it will work. Lister retrieves values from the database. This field has no database table. I'll have a look if a workaround is possible but you might have to wait a long time. PRs welcome though :-).

Link to comment
Share on other sites

  • 6 months later...

Today I also got the class "RuntimeMarkupUtilities" does not exist error. (At a very harmless unexpected spot, when trying to edit a simple body textarea in a template: some local field settings).

I love the RuntimeMarkup Field, never had the problem with it before and did not want to give up easily. The only difference with my other PW installs: this PW install was brand new straight from the PW github beta repo and filled with a couple of modules, including this one. When started setting up my templates, I had activated the RuntimeMarkup Module for latter use already. But had not set up a RuntimeMarkup field yet.

Now the interesting part: As soon as I had my first RuntimeMarkup field set up (just with the default settings) the "RuntimeMarkupUtilities missing" error mentioned above was gone.

Maybe the observation gives you some indication about the possible nature of the bug. Maybe there is nothing wrong with the RuntimeMarkup module code, there might be just a timing problem, or the module is looking for a  class of some target code not running because not in use at that point. Just guessing.

Link to comment
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 FireWire
      Hello community!

      I want to share a new module I've been working on that I think could be a big boost for multi-language ProcessWire sites.

      Some background, I was looking for a way for our company website to be efficiently translated as working with human translators was pretty laborious and a lack of updating content created a divergence between languages. I, and several other devs here, have talked about translation integrations and have recognized the power that DeepL has. DeepL is an AI deep learning powered service that delivers translation quality beyond any automated service available. After access to the API was opened up to the US, I built Fluency, a DeepL translation integration for ProcessWire.
      Fluency brings automated translation to every multi-language field in the admin, and also provides a translation tool allowing the user to translate their text to any language without it being inside a template's field. With Fluency you can:
      Translate any plain textarea or text input Translate any CKEditor content (yes, with markup) Translate page names for fully localized URLs on every page Translate your in-template translation function wrapped strings Translate modules Fluency is free, and now so is DeepL
      Since this module was first built DeepL has introduced free Developer accounts that allow anyone to start using Fluency at zero cost and beginning with the version 0.3.0 release Fluency now supports free DeepL accounts. As of June 2021 DeepL supports translation to 26 languages and continues to offer more!
      Installation and usage is completely plug and play. Whether you're building a new multi-language site, need to update a site to multi-language, or simply want to stop manually translating a site and make any language a one-click deal, it could not be easier to do it. Fluency works by having you match the languages configured in ProcessWIre to DeepL's. You can have your site translating to any or all of the languages DeepL translates to in minutes (quite literally).
      Let's break out the screenshots...
      When the default language tab is shown, a message is displayed to let users know that translation is available. Clicking on each tab shows a link that says "Translate from English". Clicking it shows an animated overlay with the word "Translating..." cycling through each language and a light gradient shift. Have a CKEditor field? All good. Fluency will translated it and use DeepL's ability to translate text within HTML tags. CKEditor fields can be translated as easily and accurately as text/textarea fields.

      Repeaters and AJAX created fields also have translation enabled thanks to a JavaScript MutationObserver that searches for multi-language fields and adds translation as they're inserted into the DOM. If there's a multi-language field on the page, it will have translation added.

      Same goes for image description fields. Multi-language SEO friendly images are good to go.

      Creating a new page from one of your templates? Translate your title, and also translate your page name for native language URLs. (Not available for Russian, Chinese, or Japanese languages due to URL limitations). These can be changed in the "Settings" tab for any page as well so whether you're translating new pages or existing pages, you control the URLs everywhere.

      Language configuration pages are no different. Translate the names of your languages and search for both Site Translation Files (including all of your modules)

      Translate all of the static text in your templates as well. Notice that the placeholders are retained. DeepL is pretty good at recognizing and keeping non-translatable strings like that. If it is changed, it's easy to fix manually.

      Fluency adds a "Translate" item to the CMS header. When clicked this opens up a modal with a full translation tool that lets the user translate any language to any language. No need to leave the admin if you need to translate content from a secondary language back to the default ProcessWire language. There is also a button to get the current API usage statistics. DeepL account owners can set billing limitations via character count to control costs. This may help larger sites or sites being retrofitted keep an eye on their usage. Fluency can be used by users having roles given the fluency-translate permission.

      It couldn't be easier to add Fluency to your new or existing website. Simply add your API key and you're shown what languages are currently available for translation from/to as provided by DeepL. This list and all configuration options are taken live from the API so when DeepL releases new languages you can add them to your site without any work. No module updates, just an easy configuration. Just match the language you configured in ProcessWire to the DeepL language you want it to be associated with and you're done. Fluency also allows you to create a list of words/phrases that will not be translated which can prevent items such as brands and company names from being translated when they shouldn't

       
      Limitations:
      No "translate page" - Translating multiple fields can be done by clicking multiple translation links on multiple fields at once but engineering a "one click page translate" is not feasible from a user experience standpoint. The time it takes to translate one field can be a second or two, but cumulatively that may take much longer (CKEditor fields are slower than plain text fields). There may be a workaround in the future but it isn't currently on the roadmap. No "translate site" - Same thing goes for translating an entire website at once. It would be great, but it would be a very intense process and take a very (very) long time. There may be a workaround in the future but it isn't on the roadmap. No current support for Inline CKEditor fields - Handling for CKEditor on-demand hasn't been implemented yet, this is planned for a future release though and can be done. I just forgot about it because I've never really used that feature personally.. Alpha release - This module is in alpha. Releases should be stable and usable, but there may be edge case issues. Test the module thoroughly and please report any bugs via a Github issue on the repository or respond here. Please note that the browser plugin for Grammarly conflicts with Fluency (as it does with many web applications). To address this issue it is recommended that you disable Grammarly when using Fluency, or open the admin to edit pages in a private window where Grammarly may not be loaded. This is an issue that may not have a resolution as creating a workaround may not be possible. If you have insight as to how this may be solved please visit the Github page and file a bugfix ticket.
      Requirements:
      ProcessWire  3.0+ UIKit Admin Theme That's Fluency in a nutshell. A core effort in this module is to create it so that there is nothing DeepL related hard-coded in that would require updating it when DeepL offers new languages. I would like this to be a future-friendly module that doesn't require developer work to keep it up-to-date.
      The Module Is Free
      This is my first real module and I want to give it back to the community as thanks. This is the best CMS I've worked with (thank you Ryan & contributors) and a great community (thank you dear reader).
      DeepL Developer Accounts
      In addition to paid Pro Developer accounts, DeepL now offers no-cost free accounts. Now all ProcessWire developers and users can use Fluency at no cost.
      Learn more about free and paid accounts by visiting the DeepL website. Sign up for a Developer account, get an API key, and start using Fluency today.
      Download & Feedback
      Download the latest version here
      https://github.com/SkyLundy/Fluency-Translation/archive/main.zip
      Github repository:
      https://github.com/SkyLundy/Fluency-Translation
      File issues and feature requests here (your feedback and testing is greatly appreciated):
      https://github.com/SkyLundy/Fluency-Translation/issues
       
      Thank you! ¡Gracias! Ich danke Ihnen! Merci! Obrigado! Grazie! Dank u wel! Dziękuję! Спасибо! ありがとうございます! 谢谢你!

    • By monollonom
      (once again I was surprised to see a work of mine pop up in the newsletter, this time without even listing the module on PW modules website 😅. Thx @teppo !)
      Github: https://github.com/romaincazier/FieldtypeQRCode
      Modules directory: https://processwire.com/modules/fieldtype-qrcode/
      This is a simple module I made so a client could quickly grab a QR Code of the page's url in the admin.
      There's not much to it for now, but if need be you can output anything using a hook:
      $wire->addHookAfter("FieldtypeQRCode::getQRText", function($event) { $event->return = "Your custom text"; }) You can also output the QR code on your front-end by calling the field:
      echo $page->qr_code_field; The module uses the PHP library QR Code Generator by Kazuhiko Arase. When looking for a way to generate a QR Code in PW I came across @ryan's integration in his TFA module. I'm not very familiar with fieldtype/inputfield module development so I blindly followed @bernhard (great) tutorial and his BaseFieldtypeRuntime. At some point I'll take a deeper look to make a module on my own.
      Some ideas for improvements :
      add the ability to choose what to ouput : page's url / editUrl / file(s) / image(s) / ... allow to output multiple QR codes ?
    • By Chris Bennett
      https://github.com/chrisbennett-Bene/AdminThemeTweaker
      Inspired by @bernhard's excellent work on the new customisable LESS CSS getting rolled into the core soon, I thought I would offer up the module for beta testing, if it is of interest to anyone.

      It takes a different approach to admin styling, basically using the Cascade part of CSS to over-ride default UiKit values.
      Values are stored in ModuleConfig Module creates a separate AdminThemeTweaker Folder at root, so it can link to AdminThemeTweaker.php as CSS AdminThemeTweaker.php reads the module values, constructs the CSS variables then includes the CSS framework Can be switched on and off with a click. Uninstall removes everything, thanks to bernhard's wonderful remove dir & contents function.
      It won't touch your core. It won't care if stuff is upgraded. You won't need to compile anything and you don't need to touch CSS unless you want to.

      It won't do much at all apart from read some values from your module config, work out the right CSS variables to use (auto contrast based on selected backgrounds) and throw it on your screen.
      You can configure a lot of stuff, leave it as it comes (dark and curvy), change two main colors (background and content background) or delve deep to configure custom margins, height of mastheads, and all manner of silly stuff I never use.

      Have been developing it for somewhere around 2 years now. It has been (and will continue to be) constantly tweaked over that time, as I click on something and find something else to do.
      That said, it is pretty solid and has been in constant use as my sole Admin styling option for all of those 2 years.

      If nothing else, it would be great if it can provide any assistance to @bernhard or other contributor's who may be looking to solve some of the quirkier UiKit behavior.
      Has (in my opinion) more robust and predictable handling of hidden Inputfields, data-colwidths and showIf wrappers.
      I am very keen to help out with that stuff in any way I can, though LESS (and any css frameworks/tools basically) are not my go.
      I love CSS variables and banging-rocks-together, no-dependency CSS you can write with notepad.



       

    • By opalepatrick
      I see old posts saying that repeaters are not the way to go in Custom Process Modules. If that is the case, when using forms (as I am trying to do) how would one tackle things like repeat contact fields where there can be multiple requirements for contact details with different parameters? (Like point of contact, director, etc) or even telephone numbers that have different uses?
      Just for background I am creating a process module that allows me to create types of financial applications in the admin area (no need to publish any of this, pure admin) that require a lot of personal or company information.
      Maybe I am thinking about this incorrectly?
    • By HMCB
      I ran across a reference to IftRunner module. The post was 6 years ago. I cant find it in available modules. Has it been pulled?
×
×
  • Create New...