<?xml version="1.0"?>
<rss version="2.0"><channel><title>Module/Plugin Development Latest Topics</title><link>https://processwire.com/talk/forum/19-moduleplugin-development/</link><description>Module/Plugin Development Latest Topics</description><language>en</language><item><title>FrontendForms - A module for creating and validating forms on the frontend</title><link>https://processwire.com/talk/topic/26015-frontendforms-a-module-for-creating-and-validating-forms-on-the-frontend/</link><description><![CDATA[<p>
	Hello <span>@ all </span>
</p>

<p>
	I want to share a new module with you, which makes the creation and validation of forms easy.  Take a look at the following example of a simple contact form:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">// A very simple example of a contactform for demonstration purposes

$form = new Form('contactform');

$gender = new Select('gender');
$gender-&gt;setLabel('Gender');
$gender-&gt;addOption('Mister', '0');
$gender-&gt;addOption('Miss', '1');
$form-&gt;add($gender);

$surname = new InputText('surname');
$surname-&gt;setLabel('Surname');
$surname-&gt;setRule('required');
$form-&gt;add($surname);

$name = new InputText('name');
$name-&gt;setLabel('Name');
$name-&gt;setRule('required');
$form-&gt;add($name);

$email = new InputText('email');
$email-&gt;setLabel('E-Mail');
$email-&gt;setRule('required');
$form-&gt;add($email);

$subject = new InputText('subject');
$subject-&gt;setLabel('Subject');
$subject-&gt;setRule('required');
$form-&gt;add($subject);

$message = new Textarea('message');
$message-&gt;setLabel('Message');
$message-&gt;setRule('required');
$form-&gt;add($message);

$privacy = new InputCheckbox('privacy');
$privacy-&gt;setLabel('I accept the privacy policy');
$privacy-&gt;setRule('required')-&gt;setCustomMessage('You have to accept our privacy policy');
$form-&gt;add($privacy);

$button = new Button('submit');
$button-&gt;setAttribute('value', 'Send');
$form-&gt;add($button);

if($form-&gt;isValid()){

  print_r($form-&gt;getValues());
  // do what you want

}

// render the form
echo $form-&gt;render();</span></pre>

<p>
	This piece of code creates a simple contact form and validates it according to the validation rules set. Inside the isValid() method you can run your code (fe sending an email)
</p>

<p>
	Highlights:
</p>

<ul><li>
		30+ validation types
	</li>
	<li>
		Support for UiKit 3 and Bootstrap 5 CSS framework
	</li>
	<li>
		SPAM protection
	</li>
	<li>
		Highly customizable
	</li>
	<li>
		Hookable methods for further customization
	</li>
	<li>
		Multi-language
	</li>
</ul><p>
	You can download and find really extensive information on how to use at <a href="https://github.com/juergenweb/FrontendForms" rel="external nofollow">https://github.com/juergenweb/FrontendForms</a>.
</p>

<p>
	Please report errors or suggestions directly in GitHub.
</p>

<p>
	Best regards and happy testing <span class="ipsEmoji">?</span>
</p>

<p>
	<span style="color:#d35400;">If you have downloaded the module in the past I recommend you to uninstall the module completely and install the newest version 2.1.14. There are a lot of changes in the new version, so please test carefully.</span>
</p>
]]></description><guid isPermaLink="false">26015</guid><pubDate>Sat, 14 Aug 2021 10:59:47 +0000</pubDate></item><item><title>FrontendLoginRegister - A configurable module for fast implementation of an user management system based on FrontendForms</title><link>https://processwire.com/talk/topic/28352-frontendloginregister-a-configurable-module-for-fast-implementation-of-an-user-management-system-based-on-frontendforms/</link><description><![CDATA[<p>
	A module for ProcessWire CMS to integrate a user registration/login functionality based on the FrontendForms module. This module creates pages and templates during the installation for faster development.
</p>

<p>
	The intention for the development of such a module was to provide a ready-to-use solution for user management, which can be installed and put into operation in a few minutes.
</p>

<p>
	It works out of the box, but it provides a lot of configuration settings in the backend:
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="24669" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2023_03/module-config.jpg.f2361cbb0c724fe77a148c6113ca406d.jpg" rel=""><img alt="module-config.thumb.jpg.a85ccb0b15d57604403c399b32a31c0a.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="24669" data-ratio="113.29" style="height:auto;" width="662" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2023_03/module-config.thumb.jpg.a85ccb0b15d57604403c399b32a31c0a.jpg" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	<strong>Highlights</strong>
</p>

<ul>
	<li>
		"One-click" integration of an user login/registration system without the hazzle of creating all pages and forms by hand
	</li>
	<li>
		"One-click" switch between login or login and registration option
	</li>
	<li>
		Double opt-in with activation link on new registrations
	</li>
	<li>
		Option for automatic sending of reminder mails, if account activation is still pending
	</li>
	<li>
		Option for automatic deletion of unverified accounts after a certain time to prevent unused accounts
	</li>
	<li>
		Option to use TFA-Email if installed for higher security on login
	</li>
	<li>
		Mutli-language
	</li>
	<li>
		Select if you want to login with username and password or email and password
	</li>
	<li>
		Select the desired roles for newly created users
	</li>
	<li>
		Select, which fields of the user template should be displayed on the registration and profile form (beside the mandatory fields). Fields and order can be changed via drag and drop functionality
	</li>
	<li>
		Offer users the possibility to delete their account in the members area using a deletion link with time expiration
	</li>
	<li>
		Customize the texts of the emails which will be send by this module
	</li>
	<li>
		Usage of all the benefits of FrontendForms (fe. CAPTCHA, various security settings,...)
	</li>
	<li>
		Support for SeoMaestro if installed
	</li>
	<li>
		Lock accounts if suspicious login attempts were made
	</li>
	<li>
		Support of Ajax form submission
	</li>
</ul>

<p>
	This module runs on top of the <a href="https://processwire.com/modules/frontend-forms/" rel="external">FrontendForms module</a>, so please download and install this module first.
</p>

<p>
	UPDATE 03.11.2023: The module is now available inside the Processwire modules directory: <a href="https://processwire.com/modules/frontend-login-register/" rel="external">Frontendloginregister</a>
</p>

<p>
	This module is early Beta stage, so please do not use it on live sites at the moment.
</p>

<p>
	If you discover any issues, please report them directly on GitHub <span class="ipsEmoji">?</span>. Thanks!
</p>
]]></description><guid isPermaLink="false">28352</guid><pubDate>Thu, 30 Mar 2023 06:12:34 +0000</pubDate></item><item><title>Native Analytics  &#x2014; a native analytics module for ProcessWire</title><link>https://processwire.com/talk/topic/31808-native-analytics-%E2%80%94-a-native-analytics-module-for-processwire/</link><description><![CDATA[<p>
	Hi everyone,
</p>

<p>
	I’m happy to share <strong>NativeAnalytics</strong>, a native first-party analytics module for ProcessWire.
</p>

<p>
	The module is now available in the ProcessWire modules directory:<br />
	<a href="https://processwire.com/modules/native-analytics/" rel="external">https://processwire.com/modules/native-analytics/</a>
</p>

<p>
	NativeAnalytics provides a useful analytics dashboard directly inside the ProcessWire admin, without relying on external analytics platforms, third-party scripts or external APIs. All analytics data is stored locally in your ProcessWire installation, which makes it a good fit for projects where you want a simpler, more self-contained analytics solution.
</p>

<p>
	The module currently tracks and displays:
</p>

<ul>
	<li>
		page views
	</li>
	<li>
		unique visitors
	</li>
	<li>
		sessions
	</li>
	<li>
		current visitors
	</li>
	<li>
		top pages
	</li>
	<li>
		referrers
	</li>
	<li>
		devices and browsers
	</li>
	<li>
		404 hits
	</li>
	<li>
		engagement events such as form submits, downloads, tel/mail clicks, outbound clicks and custom CTA events
	</li>
</ul>

<p>
	It also includes:
</p>

<ul>
	<li>
		charts and trend views
	</li>
	<li>
		comparison between periods
	</li>
	<li>
		custom date range filtering
	</li>
	<li>
		page-level analytics inside the page edit screen
	</li>
	<li>
		optional monthly email reports
	</li>
	<li>
		optional PDF report attachments
	</li>
	<li>
		exports to CSV, PDF and DOCX
	</li>
	<li>
		helper examples and a small snippet generator for custom event tracking
	</li>
</ul>

<p>
	New in the latest version:
</p>

<ul>
	<li>
		Goals and conversion tracking
	</li>
	<li>
		event-based goals, for example form submits, CTA clicks, downloads or tel/mail clicks
	</li>
	<li>
		page/path-based goals, for example thank-you pages or booking confirmation pages
	</li>
	<li>
		conversion rate reporting based on sessions and unique visitors
	</li>
	<li>
		a dedicated Goals dashboard with goal cards, goal trends and goal overview
	</li>
	<li>
		easier goal setup with helper text, quick presets and suggestions from already tracked events and pages
	</li>
	<li>
		daily aggregate tables for events and goals
	</li>
	<li>
		raw event retention setting
	</li>
	<li>
		additional database indexes and cleanup helpers for larger datasets
	</li>
</ul>

<p>
	There are also several privacy and consent-related options:
</p>

<ul>
	<li>
		optional cookie-less visitor/session mode
	</li>
	<li>
		consent-based tracking
	</li>
	<li>
		helper functions for custom consent integrations
	</li>
	<li>
		optional PrivacyWire localStorage consent helper support
	</li>
	<li>
		cleaner behaviour when global tracking is disabled
	</li>
</ul>

<p>
	 
</p>

<p>
	The reason I built this module was that I wanted something that feels natural inside ProcessWire itself, instead of embedding another analytics service into the admin. For many sites, it can be useful to have core traffic, engagement and conversion data available right where content is managed.
</p>

<p>
	Goal tracking was added because several users asked for a simple way to measure important actions without having to fight with external analytics tools. For example, you can now create a goal for a contact form submit, a CTA button click, a file download or a visit to a thank-you page, and then see conversions and conversion rates directly in the ProcessWire admin.
</p>

<p>
	 
</p>

<p>
	<strong>A small note about very large datasets:</strong> NativeAnalytics includes retention settings, daily aggregate tables and cleanup tools, and the latest version improves this further for events and goals. For very high-traffic sites, I still recommend using sensible raw data retention and keeping long-term reporting based on aggregated data. I do not want to overclaim without real long-term benchmarks on extremely large datasets, so feedback from larger installations is very welcome.
</p>

<p>
	 
</p>

<p>
	<span style="color:#c0392b;"><strong>!</strong> If you tested one of the earlier development versions named <strong>PW Native Analytics</strong>, <strong>I recommend uninstalling</strong> that old test version first and installing NativeAnalytics as a fresh module, because the module name and structure changed during development.</span>
</p>

<p>
	 
</p>

<p>
	Multi-site analytics is not included yet, but it is something I am looking into. It would need proper per-site separation in the stored analytics data, so I want to approach that carefully rather than adding a quick workaround.
</p>

<p>
	 
</p>

<p>
	Feedback, bug reports and suggestions are very welcome.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="29189" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_05/Analytics-new.jpg.8965958bdd6f348249bb7455ae92fcb7.jpg" rel=""><img alt="Analytics-new.thumb.jpg.5b37d762b5e75a21f910330ac1c7b0dc.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="29189" data-ratio="129.98" style="height:auto;" width="577" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_05/Analytics-new.thumb.jpg.5b37d762b5e75a21f910330ac1c7b0dc.jpg" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a> <a class="ipsAttachLink ipsAttachLink_image" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_05/Analytics-Dark-mode.jpg.1b14017b92b8e6731df2adcb87c3c7f5.jpg" data-fileid="29193" data-fileext="jpg" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="29193" data-ratio="149.70" width="501" alt="Analytics-Dark-mode.thumb.jpg.e9bfcb460caea92fc215ecc7cde59070.jpg" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_05/Analytics-Dark-mode.thumb.jpg.e9bfcb460caea92fc215ecc7cde59070.jpg" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	Get it here:<br />
	<a href="https://processwire.com/modules/native-analytics/" rel="external">https://processwire.com/modules/native-analytics/</a>
</p>

<p>
	 
</p>

<p>
	Enjoy!
</p>

<p style="text-align:center;">
	<br />
	<br />
	If you find NativeAnalytics useful and would like to support further development, maintenance and testing, a small donation is always appreciated.<br />
	The module will remain free, but support helps me spend more time improving it and adding new features.<br />
	<a href="https://www.paypal.com/donate/?business=BBZT3L4ES7U7A&amp;no_recurring=0&amp;item_name=Free+open-source+modules%2C+built+in+my+spare+time.+If+they+help+your+projects%2C+a+small+donation+keeps+development+going.+Thanx%21&amp;currency_code=EUR" rel="external nofollow">DONATE</a>
</p>

<p>
	 
</p>

<p>
	 
</p>
]]></description><guid isPermaLink="false">31808</guid><pubDate>Fri, 10 Apr 2026 17:53:47 +0000</pubDate></item><item><title>ProcessSiteSettings &#x2013; simple site-wide settings for ProcessWire</title><link>https://processwire.com/talk/topic/31775-processsitesettings-%E2%80%93-simple-site-wide-settings-for-processwire/</link><description><![CDATA[<p>
	I made a small module called ProcessSiteSettings and thought I’d share it here in case it’s useful to someone.<br />
	<br />
	<strong>ProcessSiteSettings</strong> is a lightweight ProcessWire module for storing and editing global site values in one place. There are already similar modules available, so this is simply another free option with a focus on quick setup, usability, and helpful template snippets.
</p>

<p>
	It adds a <strong>Settings </strong>item to the ProcessWire admin menu and creates one central page for global site values like:
</p>

<ul>
	<li>
		copyright text
	</li>
	<li>
		footer content
	</li>
	<li>
		contact info
	</li>
	<li>
		social links
	</li>
	<li>
		SEO / AI summary text
	</li>
	<li>
		images
	</li>
	<li>
		repeaters
	</li>
	<li>
		Repeater Matrix
	</li>
	<li>
		page references
	</li>
	<li>
		other regular PW fields<br />
		Ofc. add your own fields of choice to it, or delete the ones you don't need!
	</li>
</ul>

<p>
	It uses a normal <strong>template + page</strong> approach instead of a fake config form, so it works much more naturally with ProcessWire fields.
</p>

<p>
	It also includes a small helper box on the settings edit screen that shows ready-to-use template snippets for each field. It tries to generate smarter examples depending on the field type, including more complex fields like images, repeaters, matrix items, multi-value fields, etc.
</p>

<p>
	<br />
	Settings Menu:
</p>

<p>
	<img alt="Menu.jpg.ee678e2a89c908b45035afe8935241b6.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="28826" data-ratio="71.56" style="height:auto;" width="770" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_03/Menu.jpg.ee678e2a89c908b45035afe8935241b6.jpg" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /><br />
	<br />
	Editing Values, adding/deleting custom fields and shortcut to Fields for quick creation of new ones.<br />
	<span><img alt="Edit_Value.thumb.jpg.aedca950c9c941d228b273c37ab88059.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="28824" data-ratio="82.87" style="height:auto;" width="905" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_03/Edit_Value.thumb.jpg.aedca950c9c941d228b273c37ab88059.jpg" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></span>
</p>

<p>
	<br />
	<span>Quick helper for showing everything on frontend!</span>
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="28825" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_03/Helpers.jpg.4955bc9d015f42428660c92aa35fede7.jpg" rel=""><img alt="Helpers.thumb.jpg.999dfb8f867ef7eb0aa78e432ca02ab7.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="28825" data-ratio="73.50" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_03/Helpers.thumb.jpg.999dfb8f867ef7eb0aa78e432ca02ab7.jpg" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a><br />
	<br />
	<br />
	Example usage on templates:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted"><span class="pun">&lt;?=</span><span class="pln"> siteSettings</span><span class="pun">(</span><span class="str">'ss_copyright_text'</span><span class="pun">);</span><span class="pln"> </span><span class="pun">?&gt;</span><span class="pln">
</span><span class="pun">&lt;?=</span><span class="pln"> siteSettings</span><span class="pun">()-&gt;</span><span class="pln">ss_footer_text</span><span class="pun">;</span><span class="pln"> </span><span class="pun">?&gt;</span><span class="pln">
</span><span class="pun">&lt;?=</span><span class="pln"> $siteSettings</span><span class="pun">-&gt;</span><span class="pln">ss_contact_email</span><span class="pun">;</span><span class="pln"> </span><span class="pun">?&gt;</span></pre>

<p>
	The admin is in English and the module is translation-ready.<br />
	<br />
	Just install the module, and that's it!
</p>

<p>
	Just sharing it here in case someone finds it useful.<br />
	<br />
	Download Here:<br />
	<a class="ipsAttachLink" data-fileid="29527" href="https://processwire.com/talk/applications/core/interface/file/attachment.php?id=29527&amp;key=c06965b262e5ef05d44bcbed1060aa11" data-fileext="zip" rel="">ProcessSiteSettings-v13-fixed.zip</a>
</p>

<p>
	 
</p>
]]></description><guid isPermaLink="false">31775</guid><pubDate>Tue, 17 Mar 2026 19:15:26 +0000</pubDate></item><item><title>[WIP] Cursor MCP to Processwire (incl. new UI option)</title><link>https://processwire.com/talk/topic/31647-wip-cursor-mcp-to-processwire-incl-new-ui-option/</link><description><![CDATA[<p>
	I wanted a way to chat with my Processwire site and built a Module (PW MCP) and an MCP server to connect into it.<br />
	<br />
	It's a private repo at the moment but it can be public if anyone finds it useful. It's basically a way to use the Cursor Chat Ui to query my site, fields, templates and content. <br />
	Here's part of the readme which explains it better.<br />
	<br />
	<strong>What Is It?</strong><br />
	ProcessWire MCP is a bridge between ProcessWire and Cursor IDE (the AI-powered code editor). It lets you query your ProcessWire site's structure and content directly from Cursor's chat interface using natural language.<br />
	Instead of writing selectors or browsing the admin, you can just ask:
</p>

<ul>
	<li>
		"What templates does this site have?"
	</li>
	<li>
		"Show me the fields on the blog-post template"
	</li>
	<li>
		"Search for pages containing 'summer'"
	</li>
	<li>
		"Find all images with 'lake' in the filename"
	</li>
</ul>

<p>
	<br />
	<strong>Why I Built It</strong><br />
	Cursor can see your template files and code in the local directory, but it can't see what's actually in your ProcessWire database — which templates and fields are registered, what pages exist, or what content they contain.
</p>

<p>
	With ProcessWire MCP, the AI can:
</p>

<ul>
	<li>
		Query the actual database schema (not just parse template files)
	</li>
	<li>
		Look up page content by ID, path, or selector
	</li>
	<li>
		Understand field configurations (types, settings, which templates use them)
	</li>
	<li>
		Search across all text content and find files/images
	</li>
	<li>
		Get RepeaterMatrix content with type labels
	</li>
	<li>
		See file metadata (dimensions, descriptions, URLs)
	</li>
</ul>

<p>
	It's the difference between seeing $page-&gt;body in code vs. knowing what that page's body actually contains.
</p>

<p>
	<strong>Architecture</strong>
</p>

<p>
	Cursor Chat → MCP Server (Node.js) → PHP CLI → ProcessWire API
</p>

<p>
	The module consists of:
</p>

<ul>
	<li>
		PwMcp — A ProcessWire module with a CLI interface
	</li>
	<li>
		mcp-server — A Node.js server that speaks the Model Context Protocol
	</li>
</ul>

<p>
	The CLI can also be used standalone for quick queries from terminal.
</p>

<p>
	<strong>Available Commands</strong><br />
	 
</p>

<table style="background-color:#ffffff;border-collapse:collapse;color:#000000;font-size:13px;">
	<thead>
		<tr>
			<th style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span>Command</span>
			</th>
			<th style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Description</span>
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>health</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Check connection</span><span><span> </span>and get</span><span><span> </span>site</span><span><span> </span>info</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>list</span><span>-</span><span>templates</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>List all templates with</span><span><span> </span>fiel</span><span>d counts</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>get</span><span>-template [</span><span>name</span><span>]</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Get template details</span><span><span> </span>an</span><span>d fields</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>list</span><span>-fields</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>List all fields with</span><span><span> </span>types</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>get-field [name</span><span>]</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Get field details an</span><span>d usage</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>get-page [i</span><span>d\</span><span>|path]</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Get page by</span><span><span> </span>ID or path with</span><span><span> </span>all field values</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>query</span><span>-pages [</span><span>selector</span><span>]</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Query pages using</span><span><span> </span>P</span><span>W sel</span><span>ectors</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>search</span><span><span> </span>[</span><span>query</span><span>]</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Search content</span><span><span> </span>across all text</span><span><span> </span>fields</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:1px solid;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>search</span><span>-files [query]</span></span>
			</td>
			<td style="border-bottom:1px solid;border-right:none;padding:5px 9px;text-align:left;">
				<span>Search</span><span><span> </span>files by</span><span><span> </span>name/extension</span>
			</td>
		</tr>
		<tr>
			<td style="border-bottom:none;border-right:1px solid;padding:5px 9px;text-align:left;">
				<span style="color:inherit;"><span>export</span><span>-schema</span></span>
			</td>
			<td style="border-bottom:none;border-right:none;padding:5px 9px;text-align:left;">
				<span>Export complete</span><span><span> </span>site schema</span>
			</td>
		</tr>
	</tbody>
</table>

<p>
	<strong>Example: Health Check</strong>
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted"><span class="pln">php site</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="typ">PwMcp</span><span class="pun">/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">pw</span><span class="pun">-</span><span class="pln">mcp</span><span class="pun">.</span><span class="pln">php health </span><span class="pun">--</span><span class="pln">pretty</span></pre>

<pre class="ipsCode">{
  "status": "ok",
  "pwVersion": "3.0.241",
  "siteName": "www.example.com",
  "moduleLoaded": true,
  "counts": {
    "templates": 45,
    "fields": 72,
    "pages": 960
  }
}</pre>

<p>
	<strong>Example: Content Search</strong><br />
	Ask Cursor: "Search for pages containing 'summer'"
</p>

<pre class="ipsCode">{
  "query": "summer",
  "count": 5,
  "results": [
    {
      "id": 1764,
      "title": "Lake District walks in summer",
      "path": "/guides/lake-district-summer/",
      "template": "page-guide",
      "matchedField": "Body",
      "snippet": "The Lake District offers some of the best walking trails in summer. From gentle lakeside strolls to challenging fell walks..."
    }
  ]
}</pre>

<p>
	<strong>Example: File Search</strong><br />
	Ask Cursor: "Find images with 'lake' in the filename"
</p>

<pre class="ipsCode">{
  "query": "lake",
  "count": 5,
  "results": [
    {
      "filename": "lake-windermere-sunset.jpg",
      "url": "/site/assets/files/1070/lake-windermere-sunset.jpg",
      "size": 31207,
      "sizeStr": "30.5 kB",
      "description": "Sunset over Lake Windermere",
      "field": "Images",
      "page": {
        "id": 1070,
        "title": "Lake District walks in summer",
        "path": "/guides/lake-district-summer/"
      },
      "width": 500,
      "height": 626
    }
  ]
}</pre>

<p>
	<strong>Example: Get Page with All Fields</strong><br />
	Ask Cursor: "Get the page at /about/"
</p>

<pre class="ipsCode">{
  "id": 1050,
  "name": "about",
  "path": "/about/",
  "url": "/about/",
  "template": "basic-page",
  "status": 1,
  "statusName": "published",
  "parent": {
    "id": 1,
    "path": "/",
    "title": "Home"
  },
  "numChildren": 5,
  "created": "2023-05-15T10:30:00+00:00",
  "modified": "2024-11-20T14:22:00+00:00",
  "fields": {
    "title": "About Us",
    "body": "&lt;p&gt;We are a team of dedicated professionals...&lt;/p&gt;",
    "Images": {
      "_count": 2,
      "_files": ["team-photo.jpg", "office.jpg"]
    }
  }
}</pre>

<p>
	<strong>Example: RepeaterMatrix Support</strong><br />
	The module fully supports RepeaterMatrix fields, returning the actual content with type labels:
</p>

<pre class="ipsCode">{
  "matrix": {
    "_count": 3,
    "_items": [
      {
        "_typeId": 1,
        "_typeLabel": "Body",
        "Body": "&lt;h2&gt;Welcome to our guide&lt;/h2&gt;&lt;p&gt;This guide covers...&lt;/p&gt;",
        "Images": null
      },
      {
        "_typeId": 2,
        "_typeLabel": "FAQs",
        "faq_question": "What is the best time to visit?",
        "faq_answer": "The summer months offer the best weather for walking..."
      },
      {
        "_typeId": 3,
        "_typeLabel": "Call to Action",
        "cta_title": "Plan Your Visit",
        "cta_link": "/contact/"
      }
    ]
  }
}</pre>

<p>
	So thats the first part done and working.<br />
	<br />
	My next plan is to be able to 
</p>

<p>
	1. PULL / convert a databse page into a local text file which lists all page properties, fields, template etc<br />
	2. edit the file as a local text file<br />
	3 PUSH the text file back into PW so that the original content picks up the changes
</p>

<p>
	Just having fun and building something useful. Very likely there are similar solutions or better ways to handle this but this suits my workflow ATM.<br />
	Cheers<br />
	P
</p>

<p>
	<br />
	 
</p>
]]></description><guid isPermaLink="false">31647</guid><pubDate>Thu, 29 Jan 2026 17:01:14 +0000</pubDate></item><item><title>[WIP] WireBooking &#x2013; A lightweight, self-hosted appointment booking system</title><link>https://processwire.com/talk/topic/31615-wip-wirebooking-%E2%80%93-a-lightweight-self-hosted-appointment-booking-system/</link><description><![CDATA[<p>
	Hi everyone! 
</p>

<p>
	I am currently developing a new module for a client project and wanted to quickly reach out to see if there is broader interest in the community for a solution like this.
</p>

<p>
	<b>The Use Case</b> My client needed an appointment booking system similar to "Calendly". However, they had specific requirements:
</p>

<ol start="1">
	<li>
		<p>
			<b>Zero external dependencies:</b> No third-party SaaS for GDPR/DSGVO compliance and to avoid monthly fees.
		</p>
	</li>
	<li>
		<p>
			<b>Full Design Control:</b> It had to fit seamlessly into their custom design.
		</p>
	</li>
	<li>
		<p>
			<b>Lightweight:</b> No heavy bloat.
		</p>
	</li>
</ol>

<p>
	<b>The Solution: </b><b>WireBooking</b> is a native ProcessWire module that handles appointment slots and bookings using standard ProcessWire pages.
</p>

<p>
	<b>Current Features:</b>
</p>

<ul>
	<li>
		<p>
			<b>Frontend Wizard:</b> An interactive, step-by-step booking process built with <b>AlpineJS</b> and <b>Tailwind CSS</b>.
		</p>
	</li>
	<li>
		<p>
			<b>Native Storage:</b> Bookings are saved as standard ProcessWire pages (<code>booking-entry</code>), allowing you to use the full power of PW selectors and hooks.
		</p>
	</li>
	<li>
		<p>
			<b>Backend Management:</b> Simple interface using the native ProcessWire Admin Theme (UIkit) to view bookings.
		</p>
	</li>
	<li>
		<p>
			<b>Notifications:</b> Sends confirmation emails to the customer and admin, including generated <b>.ics calendar files</b> for Outlook/Apple/Google Calendar.
		</p>
	</li>
	<li>
		<p>
			<b>Availability Management:</b> Manually block specific time slots or entire date ranges via the module settings.
		</p>
	</li>
	<li>
		<p>
			<b>AJAX Driven:</b> Dynamically loads available slots via JSON to keep the initial page load light.
		</p>
	</li>
</ul>

<p>
	<b>The "Catch" (Requirements)</b> To keep the module lightweight and modern, it is opinionated regarding the frontend stack. It assumes you are already using (or are willing to include):
</p>

<ul>
	<li>
		<p>
			<b>Tailwind CSS</b> (Utility classes) for the styling.
		</p>
	</li>
</ul>

<p>
	<b>Usage Example:</b> Using it in a template is extremely simple:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted"><span class="pun">&lt;?</span><span class="pln">php echo $modules</span><span class="pun">-&gt;</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'WireBooking'</span><span class="pun">)-&gt;</span><span class="pln">renderWizard</span><span class="pun">();</span><span class="pln"> </span><span class="pun">?&gt;</span></pre>

<p>
	<b>I need your feedback!</b> The module is currently functional for this specific use case (Consultants/Service Providers). Before I invest time into generalizing it for a public release on the modules directory, I have two questions for you:
</p>

<ol start="1">
	<li>
		<p>
			<b>Is this something you would use?</b> Is there a need for a native "Calendly" alternative?
		</p>
	</li>
	<li>
		<p>
			<b>Is the dependency on Tailwind a dealbreaker?</b> Since the markup relies on Tailwind utility classes, it might be hard to style if you use Bootstrap or custom CSS.
		</p>
	</li>
</ol>

<p>
	Looking forward to your thoughts and suggestions!
</p>

<p>
	Cheers, Markus
</p>

<p>
	 
</p>

<p>
	<strong>GitHub:</strong> <a href="https://github.com/markusthomas/WireBooking" rel="external nofollow">https://github.com/markusthomas/WireBooking</a>
</p>

<p>
	<img alt="image.png.1ba8dab9bb03e8b4680d1c39fd46349d.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28633" data-ratio="41.63" style="height:auto;" width="711" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.png.1ba8dab9bb03e8b4680d1c39fd46349d.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /><img alt="image.png.766a40a8b3c937cd457afc4ad7c0866d.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28634" data-ratio="64.91" style="height:auto;" width="701" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.png.766a40a8b3c937cd457afc4ad7c0866d.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" />
</p>

<p>
	<img alt="image.png.edd81bf26d0e297a7efcef1abbb2f5fc.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28635" data-ratio="53.75" style="height:auto;" width="720" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.png.edd81bf26d0e297a7efcef1abbb2f5fc.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /><img alt="image.png.6c9475f6e680f343ca7e39688f93e80e.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28636" data-ratio="64.75" style="height:auto;" width="729" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.png.6c9475f6e680f343ca7e39688f93e80e.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" />
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="28637" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.png.0e29dc6bacf5ce29c95917c0078d9aaf.png" rel=""><img alt="image.thumb.png.fa328f168c5b113bc94227cf340ed688.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28637" data-ratio="57.60" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.thumb.png.fa328f168c5b113bc94227cf340ed688.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="28639" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.png.06e53e247d45e6eadd36eea66eb0f0f9.png" rel=""><img alt="image.thumb.png.af434bf9d3a05e9aec3d1d75ff4ef5e2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28639" data-ratio="34.30" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_01/image.thumb.png.af434bf9d3a05e9aec3d1d75ff4ef5e2.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>
]]></description><guid isPermaLink="false">31615</guid><pubDate>Sat, 10 Jan 2026 08:17:28 +0000</pubDate></item><item><title>ProcessWire Dashboard</title><link>https://processwire.com/talk/topic/22847-processwire-dashboard/</link><description><![CDATA[<p>
	<span style="font-size:20px;"><strong>ProcessWire Dashboard</strong></span>
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="18610" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/1577998373376.jpg.d1fa659a37cb8d0b8cfc3438879b2239.jpg" rel=""><img alt="1577998373376.thumb.jpg.0d9081029323d8788ec2e8e84662ae7c.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="18610" data-ratio="68.8" width="1000" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/1577998373376.thumb.jpg.0d9081029323d8788ec2e8e84662ae7c.jpg"></a>
</p>

<p>
	<span style="font-size:20px;"><strong>Download</strong></span>
</p>

<p>
	You can find <a href="https://github.com/daun/processwire-dashboard/releases" rel="external nofollow">the latest release on Github</a>.
</p>

<p>
	<span style="font-size:20px;"><strong>Documentation</strong></span>
</p>

<p>
	<a href="https://daun.github.io/processwire-dashboard/" rel="external nofollow">Check out the documentation</a> to get started. This is where you'll find information about <a href="https://daun.github.io/processwire-dashboard/#/panels" rel="external nofollow">included panel types</a> and <a href="https://daun.github.io/processwire-dashboard/#/getting-started?id=configuring-panels" rel="external nofollow">configuration options</a>.
</p>

<p>
	<span style="font-size:20px;"><strong>Custom Panels</strong></span>
</p>

<p>
	The goal was to make it simple to create custom panels. The easiest way to do that is to use the panel type <span style="font-family:'Courier New', Courier, monospace;">template</span> and have it render a file in your templates folder. This might be enough for 80% of all use cases. For anything more complex (FormBuilder submissions? Comments? Live chat?), you can add new panel types by creating modules that extend the <span style="font-family:'Courier New', Courier, monospace;">DashboardPanel</span> base class. Check out the <a href="https://daun.github.io/processwire-dashboard/#/panels/custom" rel="external nofollow">documentation on custom panels</a> or take a look at the <a href="https://github.com/daun/processwire-dashboard/blob/develop/DashboardPanelHelloWorld.module" rel="external nofollow">HelloWorld panel</a> to get started. I'm happy to merge any user-created modules into the main repo if they might be useful to more than a few people.
</p>

<p>
	<span style="font-size:20px;"><strong>Roadmap</strong></span>
</p>

<ul><li>
		<strong>Panel types</strong>

		<ul><li>
				Google Analytics
			</li>
			<li>
				Draft
			</li>
			<li>
				At a glance / Page counter
			</li>
			<li>
				404s
			</li>
		</ul></li>
	<li>
		<strong><span> </span>Layout options</strong>
		<ul><li>
				Render multiple tabs per panel
			</li>
		</ul></li>
	<li>
		<strong>Chart panel</strong>
		<ul><li>
				load chart data from JS file (currently passed as PHP array)
			</li>
		</ul></li>
</ul>]]></description><guid isPermaLink="false">22847</guid><pubDate>Thu, 02 Jan 2020 21:27:58 +0000</pubDate></item><item><title>[WIP] Media Hub - the centralised hub for your PW media</title><link>https://processwire.com/talk/topic/31726-wip-media-hub-the-centralised-hub-for-your-pw-media/</link><description><![CDATA[<p>
	Hey, I've started to create a Media Hub for Processwire.<br />
	<br />
	[Edit...newest updates to UI are in later posts]<br />
	<br />
	Screenshots attached. Obviously a few UI improvements are needed <span class="ipsEmoji">🙂</span> <br />
	<br />
	One of my clients requested a centralised media manager. I thought it'd be fun to give it a go and learn some stuff.<br />
	<br />
	I know that with a self-built Module, it'll always be maintained, and I have an active interest in evolving it.<br />
	<br />
	Shout out to <a contenteditable="false" data-ipshover="" data-ipshover-target="https://processwire.com/talk/profile/3543-markus-th/?do=hovercard" data-mentionid="3543" href="https://processwire.com/talk/profile/3543-markus-th/" rel="">@markus-th</a> who just announced he is doing similar with <a href="https://processwire.com/talk/topic/31724-wip-wiremedia-%E2%80%93-concept-for-a-central-media-management-module/#comment-251631" rel="">WireMedia</a>.
</p>

<p>
	 
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="28689" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/PKD-mediahub-upload-main.png.dede602a3f6c761821e631d08e781e9c.png" rel=""><img alt="PKD-mediahub-upload-main.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28689" data-ratio="70.5" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/PKD-mediahub-upload-main.thumb.png.2ecf8899e0e5f2d3959a22ac1c91998d.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="28690" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/PKD-mediahub-main.png.5445f95944a52c5ffcd3dc5d6bb6d080.png" rel=""><img alt="PKD-mediahub-main.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28690" data-ratio="72.1" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/PKD-mediahub-main.thumb.png.fc23eb8054ef62ac7a7673bde7544551.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="28691" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/PKD-mediahub-upload-selection.png.0b9fa500c58b55745723ab1771f9add6.png" rel=""><img alt="PKD-mediahub-upload-selection.png" class="ipsImage ipsImage_thumbnailed" data-fileid="28691" data-ratio="135.87" style="height:auto;" width="552" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/PKD-mediahub-upload-selection.thumb.png.0fae79d6948d53075e4b05cdb8ef713e.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>
]]></description><guid isPermaLink="false">31726</guid><pubDate>Sat, 07 Feb 2026 15:57:03 +0000</pubDate></item><item><title>&#x2601;&#xFE0F; Duplicator: Backup and move sites</title><link>https://processwire.com/talk/topic/15345-%E2%98%81%EF%B8%8F-duplicator-backup-and-move-sites/</link><description><![CDATA[<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="12826" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2017_11/Duplicator-logo-v2-Dlong.png.891733350c03c3eb44ad77544a07a4c6.png" rel=""><img alt="Duplicator-logo-v2-Dlong.thumb.png.8898ec8cda79046779b10d8527fc0def.png" class="ipsImage ipsImage_thumbnailed" data-fileid="12826" data-ratio="15.9" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2017_11/Duplicator-logo-v2-Dlong.thumb.png.8898ec8cda79046779b10d8527fc0def.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	 
</p>

<ul>
	<li>
		<span style="font-size:18px;">Last update: <strong>June 2024</strong></span>
	</li>
	<li>
		<span style="font-size:18px;">Current stable version: <strong>1.5.4</strong></span>
	</li>
	<li>
		<span style="font-size:18px;">Dev version: <a href="https://github.com/flydev-fr/Duplicator/tree/dev" rel="external nofollow">github dev-branch</a></span>
	</li>
</ul>

<p>
	 
</p>

<p>
	You can find the module on Github and in the modules directory <span>:</span>
</p>

<ul>
	<li>
		<a href="https://modules.processwire.com/modules/duplicator/" rel="external">https://modules.processwire.com/modules/duplicator/</a>
	</li>
	<li>
		<a href="https://github.com/flydev-fr/Duplicator/archive/master.zip" rel="external nofollow">https://github.com/flydev-fr/Duplicator/archive/master.zip</a>
	</li>
</ul>

<p>
	 
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		Quote
	</div>

	<div class="ipsQuote_contents">
		<p>
			To restore a website, just upload <strong>the package</strong> and <strong>the installer,</strong> then go to <strong>yourwebsite.com/installer.php</strong> then follow the instructions.
		</p>

		<p>
			<span style="color:#ff0000;"><strong>PLEASE, do not forget - after the end of the process - to delete the installer.php file and your package</strong></span> 
		</p>
	</div>
</blockquote>

<p>
	 
</p>

<p>
	<strong><span style="font-size:16px;">Screenshots / Features</span></strong>
</p>

<ul>
	<li>
		Dir and files exclusion
	</li>
</ul>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="18651" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup2-feat-exclude.png.14ac60c4404492f681f674e53496930e.png" rel=""><img alt="dup2-feat-exclude.thumb.png.066de2c6ea27ae20db8abad2e91fa6a0.png" class="ipsImage ipsImage_thumbnailed" data-fileid="18651" data-ratio="57.1" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup2-feat-exclude.thumb.png.066de2c6ea27ae20db8abad2e91fa6a0.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	 
</p>

<ul>
	<li>
		CRON job
	</li>
</ul>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="18652" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup3-feat-cron.png.7749cb53cb9895c8eb8fa199775d76fb.png" rel=""><img alt="dup3-feat-cron.thumb.png.060aff48a1cdd05d9687eca51f3cab4b.png" class="ipsImage ipsImage_thumbnailed" data-fileid="18652" data-ratio="28.4" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup3-feat-cron.thumb.png.060aff48a1cdd05d9687eca51f3cab4b.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	 
</p>

<ul>
	<li>
		Advanced settings 
	</li>
</ul>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="18653" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup4-feat-adv.png.1a65189f27d49c6c34ab72097b311681.png" rel=""><img alt="dup4-feat-adv.thumb.png.9f5d8726392ad5a63e0aa1e42347a545.png" class="ipsImage ipsImage_thumbnailed" data-fileid="18653" data-ratio="28.2" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup4-feat-adv.thumb.png.9f5d8726392ad5a63e0aa1e42347a545.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	 
</p>

<ul>
	<li>
		Local and Cloud storage
	</li>
</ul>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="18654" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup5-feat-storage.png.cfee859f890fdf4c34e937ec30fc0cec.png" rel=""><img alt="dup5-feat-storage.thumb.png.35aa9c87454f472c2d04609988a42b44.png" class="ipsImage ipsImage_thumbnailed" data-fileid="18654" data-ratio="49.3" style="height:auto;" width="1000" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2020_01/dup5-feat-storage.thumb.png.35aa9c87454f472c2d04609988a42b44.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	 
</p>

<p>
	</p><video class="ipsEmbeddedVideo" controls="" data-video-embed="">
		<source type="video/mp4" data-video-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2022_03/duplicator.mp4.494ca48873c716577f2d42ebaabf9157.mp4"><a class="ipsAttachLink" data-fileext="mp4" data-fileid="22905" href="https://processwire.com/talk/applications/core/interface/file/attachment.php?id=22905&amp;key=908f8ba33dc5249ded118706909e0435" rel="">duplicator.mp4</a>
	</source></video>


<p>
	 
</p>
]]></description><guid isPermaLink="false">15345</guid><pubDate>Mon, 23 Jan 2017 07:48:47 +0000</pubDate></item><item><title>TextFormatter module best practice &#x2013; saving formatted content back to page</title><link>https://processwire.com/talk/topic/31766-textformatter-module-best-practice-%E2%80%93-saving-formatted-content-back-to-page/</link><description><![CDATA[<p>
	Hello, we've been working on a module to convert <a href="https://learn.microsoft.com/en-us/defender-office-365/safe-links-about" rel="external nofollow">Microsoft Protected Links (Safe Links)</a> so they display as their original URL.
</p>

<p>
	Protected links leak the email address of the person who copied the link into the page (eg. <a href="https://aus01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.antarctica.gov.au%2Fantarctic-operations%2F&amp;data=05%7C02%7CEXAMPLE%40domain.com%7Caf523cfcbd0b460da3e008de7eedf3e8%7Ccc58e752f96d4cb8a8251a5fa566c194%7C0%7C0%7C639087757637553262%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&amp;sdata=IgH2%2BohbmeFr7gVdzptRHiPQ6oYrR2Qt7UM2GH7fP6E%3D&amp;reserved=0" rel="external nofollow">https://aus01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.antarctica.gov.au&amp;data=05|02|EMAILADDRESS%40example.com.</a>..)
</p>

<p>
	This is not ideal, and we find more and more protected links turning up now that most of the Microsoft product offerings use protected links (Teams, Office, Copilot etc.)
</p>

<p>
	TextFormatter modules typically only reformat the text on output, leaving the page content containing the protected link. Is it acceptable for a Text Formatter module to save the formatted string back to the page? Maybe as an optional configuration in the module settings? Or, is it better to achieve this using a standard module?
</p>
]]></description><guid isPermaLink="false">31766</guid><pubDate>Tue, 10 Mar 2026 22:27:43 +0000</pubDate></item><item><title>[WIP] WireMedia &#x2013; Concept for a central media management module</title><link>https://processwire.com/talk/topic/31724-wip-wiremedia-%E2%80%93-concept-for-a-central-media-management-module/</link><description><![CDATA[<p>
	Hi everyone,
</p>

<p>
	I’m currently working on a module called <b>WireMedia Library</b> and wanted to share the current state of development to get some feedback on the concept.
</p>

<p>
	The main goal is to provide a central interface for managing media across the entire ProcessWire instance while staying 100% compatible with the native storage system.
</p>

<p>
	<b>The Concept:</b> The module acts as a central hub. You can upload files to the library and then select them from any page. When a file is selected, it is copied into the local page’s file/image field. This ensures that the files remain "native" to the page they are used on, keeping your API calls and templates exactly as they are.
</p>

<p>
	<b>Key Features of the current prototype:</b>
</p>

<ul>
	<li>
		<p>
			<b>Central Media Overview:</b> A unified view of all media assets in the system.
		</p>
	</li>
	<li>
		<p>
			<b>Usage Tracking:</b> The module indexes where files are used. Even if a file is used within a Repeater, it identifies the "leading" parent page so you know exactly where your assets live.
		</p>
	</li>
	<li>
		<p>
			<b>Database Indexing:</b> A custom database table keeps track of file locations for fast performance.
		</p>
	</li>
	<li>
		<p>
			<b>Rebuild Index:</b> A tool to rescan the system and ensure the database stays in sync with the file system.
		</p>
	</li>
	<li>
		<p>
			<b>Native Workflow:</b> Since files are copied to local fields, you can still use all native PW features (like cropping or focus points) directly on the target page.
		</p>
	</li>
</ul>

<p>
	<b>Planned:</b>
</p>

<ul>
	<li>
		<p>
			<b>Permission System:</b> Granular access control for different user roles/folders.
		</p>
	</li>
</ul>

<p>
	I am still undecided about the final licensing or if/how I will release the module, but I wanted to show the UI and the logic behind it to see if this approach resonates with the community.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/image.png.76b2a6341c0276f28697f6f1706f76bf.png" data-fileid="28688" data-fileext="png" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="28688" data-ratio="58.80" width="1000" alt="image.thumb.png.37a6d38932371fb4e643b25871781104.png" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2026_02/image.thumb.png.37a6d38932371fb4e643b25871781104.png" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" /></a>
</p>

<p>
	I’d love to hear your thoughts on the "copy-to-field" approach and the general UI!
</p>
]]></description><guid isPermaLink="false">31724</guid><pubDate>Fri, 06 Feb 2026 18:58:57 +0000</pubDate></item><item><title>WireMailBrevo</title><link>https://processwire.com/talk/topic/29912-wiremailbrevo/</link><description><![CDATA[<p>
	Just launched my first public Module <span class="ipsEmoji">?</span>
</p>

<p>
	This module allows ProcessWire to send transactional emails via <a href="https://www.brevo.com/" rel="external nofollow">Brevo</a>.
</p>

<p>
	Download the latest version: <a href="https://github.com/ttttim0709/WireMailBrevo" rel="external nofollow">https://github.com/ttttim0709/WireMailBrevo</a>
</p>

<p>
	<span style="font-size:26px;"><strong>Installation</strong></span>
</p>

<ul>
	<li>
		Copy the WireMailBrevo directory into your site/modules/ directory.
	</li>
	<li>
		In the ProcessWire admin, go to Modules &gt; Refresh.
	</li>
	<li>
		Click "Install" next to the WireMailBrevo module.
	</li>
</ul>

<p>
	<span style="font-size:26px;"><strong>Usage</strong></span>
</p>

<p>
	Example usage:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted"><span class="pln">$email </span><span class="pun">=</span><span class="pln"> $mail</span><span class="pun">-&gt;</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">
$email</span><span class="pun">-&gt;</span><span class="pln">to </span><span class="pun">=</span><span class="pln"> </span><span class="str">'recipient@somedomain.com'</span><span class="pun">;</span><span class="pln">
$email</span><span class="pun">-&gt;</span><span class="pln">subject </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Test #1'</span><span class="pun">;</span><span class="pln">
$email</span><span class="pun">-&gt;</span><span class="pln">body </span><span class="pun">=</span><span class="pln"> </span><span class="str">'An example email'</span><span class="pun">;</span><span class="pln">
$email</span><span class="pun">-&gt;</span><span class="pln">send</span><span class="pun">();</span></pre>

<p>
	<br />
	Use of Versions: <a href="https://developers.brevo.com/docs/batch-send-transactional-emails" rel="external nofollow">Check the Brevo dev section</a> for more information about Message versions
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted"><span class="pln">$email</span><span class="pun">-&gt;</span><span class="pln">versions</span><span class="pun">([</span><span class="pln">
    </span><span class="pun">[</span><span class="pln">
        </span><span class="str">'to'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="pun">[</span><span class="pln">
                </span><span class="str">'email'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'bob@example.com'</span><span class="pun">,</span><span class="pln">
                </span><span class="str">'name'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'Bob Anderson'</span><span class="pln">
            </span><span class="pun">],</span><span class="pln">
            </span><span class="pun">[</span><span class="pln">
                </span><span class="str">'email'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'anne@example.com'</span><span class="pun">,</span><span class="pln">
                </span><span class="str">'name'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'Anne Smith'</span><span class="pln">
            </span><span class="pun">],</span><span class="pln">
        </span><span class="pun">],</span><span class="pln">
        </span><span class="str">'subject'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'This is my version subject line'</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">],</span><span class="pln">
    </span><span class="pun">[</span><span class="pln">
        </span><span class="str">'to'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="pun">[</span><span class="pln">
                </span><span class="str">'email'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'jim@example.com'</span><span class="pun">,</span><span class="pln">
                </span><span class="str">'name'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'Jim Stevens'</span><span class="pln">
            </span><span class="pun">]</span><span class="pln">
        </span><span class="pun">],</span><span class="pln">
        </span><span class="str">'htmlContent'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">"&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body&gt;&lt;h1&gt;Modified header!&lt;/h1&gt;&lt;p&gt;This is still a paragraph&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;"</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">],</span><span class="pln">
</span><span class="pun">]);</span></pre>

<h2 style="border-bottom:1px solid;color:#1f2328;font-size:1.5em;">
	<span style="font-size:26px;"><strong>Configuration</strong></span>
</h2>

<p style="color:#1f2328;font-size:16px;">
	After installing the module, you can configure it by going to the module settings page. You need to provide the following configuration options:
</p>

<ul style="color:#1f2328;font-size:16px;">
	<li>
		Brevo API Key: Obtain this key from your<span> </span><a href="https://app.brevo.com/settings/keys/api" rel="external nofollow" style="background-color:transparent;">Brevo account settings</a>.
	</li>
	<li>
		Sender Email Address: The email address to be used as the sender.
	</li>
	<li>
		Sender Name: The name associated with the sender's email address.
	</li>
</ul>
]]></description><guid isPermaLink="false">29912</guid><pubDate>Fri, 12 Apr 2024 10:32:30 +0000</pubDate></item><item><title>Fieldtype module,   field names cannot be used in selectors</title><link>https://processwire.com/talk/topic/31611-fieldtype-module-field-names-cannot-be-used-in-selectors/</link><description><![CDATA[<p>
	i wrote a simple fieldtype which implements a date_from and date_to sub field. When defining a field "interval" and and then using it in a template "sampletemplate" everything works.
</p>

<p>
	In "sampletemplate.php"  fields can be accessed by $page-&gt;interval-&gt;date_from   and   $page-&gt;interval-&gt;date_to - everything fine!
</p>

<p>
	When using in a selector these fieldnames are invalid. e.g.
</p>

<p>
	$pages-&gt;find("template=sampletemplate, interval.date_to &gt; today")  throws an exception, no column with that name...
</p>

<p>
	the schema definition for the fields is
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">    public function getDatabaseSchema(Field $field) : array  {
        $schema = parent::getDatabaseSchema($field);
        $schema['data'] = 'date default null'; // date_from
        $schema['to']   = 'date default null'; // date_to
        $workspace-&gt;url = 'KEY data (`data`, `to`)';
        $schema['keys']['to']   = 'KEY `to` (`to`)';
        return $schema; 
    }</span></pre>

<p>
	i.e. date_from is mapped on standard column 'data' and date_to is mapped on 'to'.  When i use the real database column name 
</p>

<p>
	$pages-&gt;find("template=sampletemplate, interval.to &gt; today")
</p>

<p>
	it works.
</p>

<p>
	How is it possible to "correct" the names so that the name to access the field (e.g. $page-&gt;interval-&gt;data_from) and the name to be used in a selector is the same?
</p>

<p>
	and: Is there a description of the PW database schema definition to be used in a getDatabaseSchema() ?
</p>
]]></description><guid isPermaLink="false">31611</guid><pubDate>Tue, 06 Jan 2026 16:30:17 +0000</pubDate></item><item><title>[SOLVED]  Fieldtype module does not trigger sleepValue() nor wakeupValue()</title><link>https://processwire.com/talk/topic/31604-solved-fieldtype-module-does-not-trigger-sleepvalue-nor-wakeupvalue/</link><description><![CDATA[<p>
	Hello,
</p>

<p>
	i am trying to develop a simple Fieldtype module and followed <a href="https://processwire.com/talk/topic/26407-fieldtype-modules/#comment-219180" rel="">Tutorial on developing a Fieldtype module</a>.
</p>

<p>
	I installed the module (Fieldtype and Inputfield) created a field of that type, add such a field to a template and edited a page.
</p>

<p>
	After entering values and click save button the method processInput() is invoked but then nothing happens,  sleepValue() and wakeupValue() are never invoked, the resulting editor page ends up showing empty entry fields.
</p>

<p>
	What i'm getting wrong?
</p>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pun">&lt;?</span><span class="pln">php

</span><span class="kwd">namespace</span><span class="pln"> </span><span class="typ">ProcessWire</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">InputfieldTimeframe</span><span class="pln">  </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">Inputfield</span><span class="pln">  </span><span class="pun">{</span><span class="pln">

    </span><span class="com">/** 
     * getModuleInfo is a module required by all modules to tell ProcessWire about them.
     * Information about your module and some settings and definition of module dependcies
     * @return array
     */</span><span class="pln"> 
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getModuleinfo</span><span class="pun">()</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="str">'title'</span><span class="pln">    </span><span class="pun">=&gt;</span><span class="pln"> __</span><span class="pun">(</span><span class="str">'Inputfield Timeframe'</span><span class="pun">,</span><span class="pln">                   __FILE__</span><span class="pun">),</span><span class="pln">
            </span><span class="str">'summary'</span><span class="pln">  </span><span class="pun">=&gt;</span><span class="pln"> __</span><span class="pun">(</span><span class="str">'Inputfield for start date and end date'</span><span class="pun">,</span><span class="pln"> __FILE__</span><span class="pun">),</span><span class="pln">
            </span><span class="str">'author'</span><span class="pln">   </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'T. Heinlein'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'version'</span><span class="pln">  </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">"0.1.2"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'icon'</span><span class="pln">     </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'calendar'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'permanent'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'requires'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
                </span><span class="str">'PHP&gt;=8.0.0'</span><span class="pun">,</span><span class="pln">
                </span><span class="str">'ProcessWire&gt;=3.0.244'</span><span class="pun">,</span><span class="pln">
                </span><span class="str">'FieldtypeTimeframe'</span><span class="pln">
            </span><span class="pun">],</span><span class="pln">
        </span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> __construct</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        parent</span><span class="pun">::</span><span class="pln">__construct</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">



    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> ___render</span><span class="pun">()</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        $value </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">attr</span><span class="pun">(</span><span class="str">'value'</span><span class="pun">);</span><span class="pln">
        $field </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">field</span><span class="pun">;</span><span class="pln">

        $html </span><span class="pun">=</span><span class="pln"> </span><span class="str">"&lt;div class='interval'&gt;"</span><span class="pln">
        </span><span class="pun">.</span><span class="pln"> </span><span class="str">"&lt;input type='date' name='datefrom'&gt;"</span><span class="pln">
        </span><span class="pun">.</span><span class="pln"> </span><span class="str">"&lt;input type='date' name='dateto'&gt;"</span><span class="pln">
        </span><span class="pun">.</span><span class="pln"> </span><span class="str">"&lt;/div&gt;"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"&lt;div style='border:solid 1px red;'&gt;$html&lt;/div&gt;"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> renderValue</span><span class="pun">()</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        $value </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">attr</span><span class="pun">(</span><span class="str">'value'</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"$value"</span><span class="pun">;</span><span class="pln">   </span><span class="com">// TODO:  locale format</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> ___processInput</span><span class="pun">(</span><span class="typ">WireInputData</span><span class="pln"> $input</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="kwd">self</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        $value </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">attr</span><span class="pun">(</span><span class="str">'value'</span><span class="pun">);</span><span class="pln">
        $input</span><span class="pun">-&gt;</span><span class="pln">datefrom </span><span class="pun">=</span><span class="pln"> trim</span><span class="pun">(</span><span class="pln">$input</span><span class="pun">-&gt;</span><span class="pln">datefrom</span><span class="pun">);</span><span class="pln">
        $input</span><span class="pun">-&gt;</span><span class="pln">dateto   </span><span class="pun">=</span><span class="pln"> trim</span><span class="pun">(</span><span class="pln">$input</span><span class="pun">-&gt;</span><span class="pln">dateto</span><span class="pun">);</span><span class="pln">

        $value</span><span class="pun">[</span><span class="str">'datefrom'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> $input</span><span class="pun">-&gt;</span><span class="pln">datefrom</span><span class="pun">;</span><span class="pln">
        $value</span><span class="pun">[</span><span class="str">'dateto'</span><span class="pun">]</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> $input</span><span class="pun">-&gt;</span><span class="pln">dateto</span><span class="pun">;</span><span class="pln">

        bd</span><span class="pun">(</span><span class="pln">$value</span><span class="pun">,</span><span class="pln"> </span><span class="str">"processInput"</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> $this</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pun">&lt;?</span><span class="pln">php

</span><span class="kwd">namespace</span><span class="pln"> </span><span class="typ">ProcessWire</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">FieldtypeTimeframe</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">Fieldtype</span><span class="pln">  </span><span class="pun">{</span><span class="pln">


	</span><span class="com">/** 
     * getModuleInfo is a module required by all modules to tell ProcessWire about them.
     * Information about your module and some settings and definition of module dependcies
     * @return array
     */</span><span class="pln"> 
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getModuleinfo</span><span class="pun">()</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="str">'title'</span><span class="pln">    </span><span class="pun">=&gt;</span><span class="pln"> __</span><span class="pun">(</span><span class="str">'Timeframe'</span><span class="pun">,</span><span class="pln">                           __FILE__</span><span class="pun">),</span><span class="pln">
            </span><span class="str">'summary'</span><span class="pln">  </span><span class="pun">=&gt;</span><span class="pln"> __</span><span class="pun">(</span><span class="str">'Fieldtype representing a timeframe with start and end date'</span><span class="pun">,</span><span class="pln"> __FILE__</span><span class="pun">),</span><span class="pln">
            </span><span class="str">'author'</span><span class="pln">   </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'T. Heinlein'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'version'</span><span class="pln">  </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">"0.1.1"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'icon'</span><span class="pln">     </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'calendar'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'installs'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'InputfieldTimeframe'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'requires'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
                </span><span class="str">'PHP&gt;=8.0.0'</span><span class="pun">,</span><span class="pln">
                </span><span class="str">'ProcessWire&gt;=3.0.244'</span><span class="pln">
            </span><span class="pun">]</span><span class="pln">
        </span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> __construct</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        parent</span><span class="pun">::</span><span class="pln">__construct</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="com">/**
     * define the table columns needed for that field type AND an additional column 'data' for a primary key.
     * (this is low level input for the database)
     *  *** this method MUST be implemented when writing a custom fieldtype
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getDatabaseSchema</span><span class="pun">(</span><span class="typ">Field</span><span class="pln"> $field</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> array  </span><span class="pun">{</span><span class="pln">
        $schema </span><span class="pun">=</span><span class="pln"> parent</span><span class="pun">::</span><span class="pln">getDatabaseSchema</span><span class="pun">(</span><span class="pln">$field</span><span class="pun">);</span><span class="pln">
        $schema</span><span class="pun">[</span><span class="str">'data'</span><span class="pun">]</span><span class="pln">      </span><span class="pun">=</span><span class="pln"> </span><span class="str">'varchar(64) NOT NULL'</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* data is required field for system purposes */</span><span class="pln">
        $schema</span><span class="pun">[</span><span class="str">'datefrom'</span><span class="pun">]</span><span class="pln">  </span><span class="pun">=</span><span class="pln"> </span><span class="str">'date'</span><span class="pun">;</span><span class="pln">
        $schema</span><span class="pun">[</span><span class="str">'dateto'</span><span class="pun">]</span><span class="pln">    </span><span class="pun">=</span><span class="pln"> </span><span class="str">'date'</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> $schema</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> ___sleepValue</span><span class="pun">(</span><span class="typ">Page</span><span class="pln"> $page</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Field</span><span class="pln"> $field</span><span class="pun">,</span><span class="pln"> $value</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        bd</span><span class="pun">(</span><span class="pln">$value</span><span class="pun">,</span><span class="pln"> </span><span class="str">"sleepValue"</span><span class="pun">);</span><span class="pln">

        $df </span><span class="pun">=</span><span class="pln"> $value</span><span class="pun">[</span><span class="str">'datefrom'</span><span class="pun">];</span><span class="pln">
        $dt </span><span class="pun">=</span><span class="pln"> $value</span><span class="pun">[</span><span class="str">'dateto'</span><span class="pun">];</span><span class="pln">

        $res </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'datefrom'</span><span class="pun">=&gt;</span><span class="pln"> $df</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dateto'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> $dt</span><span class="pun">];</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> $res</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> wakeupValue</span><span class="pun">(</span><span class="typ">Page</span><span class="pln"> $page</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Field</span><span class="pln"> $field</span><span class="pun">,</span><span class="pln"> $value</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        bd</span><span class="pun">(</span><span class="pln">$value</span><span class="pun">,</span><span class="pln"> </span><span class="str">"wakeupValue"</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> $value</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getBlankValue</span><span class="pun">(</span><span class="typ">Page</span><span class="pln"> $page</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Field</span><span class="pln"> $field</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> array</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getInputfield</span><span class="pun">(</span><span class="typ">Page</span><span class="pln"> $page</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Field</span><span class="pln"> $field</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        $inputfield </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">wire</span><span class="pun">(</span><span class="str">'modules'</span><span class="pun">)-&gt;</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'InputfieldTimeframe'</span><span class="pun">);</span><span class="pln">
        $inputfield</span><span class="pun">-&gt;</span><span class="kwd">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">className</span><span class="pun">();</span><span class="pln"> </span><span class="com">/* adds the class name as a css class to the wrapper element */</span><span class="pln">

        $pageValue </span><span class="pun">=</span><span class="pln"> $page</span><span class="pun">-&gt;</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">$field</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
        $inputfield</span><span class="pun">-&gt;</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">'value'</span><span class="pun">,</span><span class="pln"> $pageValue</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> $inputfield</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    

        </span><span class="com">/**
     * @param Page $page
     * @param Field $field
     * @param int|object|WireArray|string $value
     * @return int|null|object|OpeningHours|WireArray|string
     * will be called on before sleepValue and after wakeupValue
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sanitizeValue</span><span class="pun">(</span><span class="typ">Page</span><span class="pln"> $page</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Field</span><span class="pln"> $field</span><span class="pun">,</span><span class="pln"> $value</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln"> is_array</span><span class="pun">(</span><span class="pln">$value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            $value </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">getBlankValue</span><span class="pun">(</span><span class="pln">$page</span><span class="pun">,</span><span class="pln"> $field</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> $value</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>
]]></description><guid isPermaLink="false">31604</guid><pubDate>Fri, 02 Jan 2026 11:43:11 +0000</pubDate></item><item><title>[WIP/Proposal] Wire2Pdf &#x2013; A PHP 8 compatible fork of Pages2Pdf</title><link>https://processwire.com/talk/topic/31595-wipproposal-wire2pdf-%E2%80%93-a-php-8-compatible-fork-of-pages2pdf/</link><description><![CDATA[<p>
	Hi everyone,
</p>

<p>
	Like many of you, I have relied on the <b>Pages2Pdf</b> module in the past. It was a great tool, but unfortunately, it seems to be no longer actively maintained. With the shift to PHP 8 in most modern environments, the older version of the underlying mPDF library used in the original module has become a bottleneck, causing compatibility issues.
</p>

<p>
	Since I needed a working solution for a current project, I have already created a fork and updated the code to work with a newer, <b>PHP 8 compatible version of mPDF</b>.
</p>

<p>
	<b>The Idea: Wire2Pdf</b>
</p>

<p>
	I am planning to release this updated version as a new module under the name <b>Wire2Pdf</b>. This would distinguish it from the legacy module and allow for new features (like custom font support) without breaking older installations.
</p>

<p>
	<b>What I have done in a fork so far:</b>
</p>

<ul>
	<li>
		<p>
			Code refactoring for PHP 8 compatibility.
		</p>
	</li>
	<li>
		<p>
			Updated the mPDF library to a recent supported version.
		</p>
	</li>
</ul>

<p>
	-&gt; <a href="https://github.com/markusthomas/Pages2Pdf" rel="external nofollow">https://github.com/markusthomas/Pages2Pdf</a>
</p>

<p>
	<b>Before I proceed...</b>
</p>

<p>
	I wanted to check with you first:
</p>

<ol start="1">
	<li>
		<p>
			Is there still a strong interest in a maintained module based on mPDF, or have you moved to other solutions (like RockPdf)?
		</p>
	</li>
	<li>
		<p>
			Are there specific pain points you had with Pages2Pdf that I should address if I move forward with <b>Wire2Pdf</b>?
		</p>
	</li>
</ol>

<p>
	If there is enough interest, I will polish the code, package it as a new module, and release it.
</p>

<p>
	Cheers!<br />
	Markus
</p>
]]></description><guid isPermaLink="false">31595</guid><pubDate>Sat, 20 Dec 2025 10:44:26 +0000</pubDate></item><item><title>How to extend a core module class and just overwrite one method?</title><link>https://processwire.com/talk/topic/31561-how-to-extend-a-core-module-class-and-just-overwrite-one-method/</link><description><![CDATA[<p>
	I enabled a site module extending a core module:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted"><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProcessPageEditImageSelect_Pickpage</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">ProcessPageEditImageSelect</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><span class="typ">Module</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ConfigurableModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getModuleinfo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="str">'title'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'Page Edit Image pickfrompage'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'summary'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'Extends core module: Enables specifying a default page to pick images from if the page being edited has no image fields.'</span><span class="pun">,</span><span class="pln">
			</span><span class="str">'autoload'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
			</span><span class="str">'permission'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'page-edit'</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

	</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> __construct</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
		parent</span><span class="pun">::</span><span class="pln">__construct</span><span class="pun">();</span><span class="pln">
	</span><span class="pun">}</span><span class="pln">

	</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
		parent</span><span class="pun">::</span><span class="pln">init</span><span class="pun">();</span><span class="pln">
		bd</span><span class="pun">(</span><span class="pln">$this</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
	</span><span class="pun">}</span><span class="pln">

	</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> ___execute</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

	</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> getModuleConfigInputfields</span><span class="pun">(</span><span class="pln">array $data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
		$inputfields </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">wire</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InputfieldWrapper</span><span class="pun">());</span><span class="pln">
		$modules </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">wire</span><span class="pun">()-&gt;</span><span class="pln">modules</span><span class="pun">;</span><span class="pln">

		</span><span class="com">/** @var InputfieldPageListSelect $f */</span><span class="pln">
        $f </span><span class="pun">=</span><span class="pln"> $modules</span><span class="pun">-&gt;</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'InputfieldPageListSelect'</span><span class="pun">);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="pln">attr</span><span class="pun">(</span><span class="str">'name'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'defaultPage'</span><span class="pun">);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="pln">attr</span><span class="pun">(</span><span class="str">'value'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@$data</span><span class="pun">[</span><span class="str">'defaultPage'</span><span class="pun">]);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="pln">label </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">_</span><span class="pun">(</span><span class="str">'Default page for images if no image fields'</span><span class="pun">);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="kwd">value</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">@$data</span><span class="pun">[</span><span class="str">'defaultPage'</span><span class="pun">];</span><span class="pln">
		$inputfields</span><span class="pun">-&gt;</span><span class="kwd">add</span><span class="pun">(</span><span class="pln">$f</span><span class="pun">);</span><span class="pln">

        </span><span class="com">/** @var InputfieldPageListSelect $f */</span><span class="pln">
        $f </span><span class="pun">=</span><span class="pln"> $modules</span><span class="pun">-&gt;</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'InputfieldTextarea'</span><span class="pun">);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="pln">attr</span><span class="pun">(</span><span class="str">'name'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'templateDefaultPage'</span><span class="pun">);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="pln">attr</span><span class="pun">(</span><span class="str">'value'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@$data</span><span class="pun">[</span><span class="str">'templateDefaultPage'</span><span class="pun">]);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="pln">label </span><span class="pun">=</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">_</span><span class="pun">(</span><span class="str">'Default page by template for images if no image fields'</span><span class="pun">);</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="pln">notes </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1 pair of selectors `{template}:{page}` per line, e.g.,: `1:/home/`, or `home:1`, or `contact:template=home`"</span><span class="pun">;</span><span class="pln">
		$f</span><span class="pun">-&gt;</span><span class="kwd">value</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">@$data</span><span class="pun">[</span><span class="str">'templateDefaultPage'</span><span class="pun">];</span><span class="pln">
		$inputfields</span><span class="pun">-&gt;</span><span class="kwd">add</span><span class="pun">(</span><span class="pln">$f</span><span class="pun">);</span><span class="pln">

		</span><span class="kwd">return</span><span class="pln"> $inputfields</span><span class="pun">;</span><span class="pln">
	</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	where the execute method is the one <a href="https://processwire.com/talk/topic/31541-tinymce-field-in-a-repeater-insert-image-plugin-open-repeater-forpage-instead-of-current-repeater-item-page/#findComment-251016" rel="">here</a> and the init method returns the core module's data but my execute method has no effect. What's wrong with how I'm doing it?
</p>

<p>
	Thank you
</p>

<p>
	On pages where it doesn't apply, there's an error notice from the init method of the core module:  
</p>

<p>
	Modules: Failed to init module: ProcessPageEditImageSelect_Pickpage - No page specified
</p>

<p>
	but nothing shows (Tracy debugging) if not autoloaded, and ___execute has no effect either way.
</p>
]]></description><guid isPermaLink="false">31561</guid><pubDate>Fri, 28 Nov 2025 18:26:13 +0000</pubDate></item><item><title>New module type: WireMail</title><link>https://processwire.com/talk/topic/5693-new-module-type-wiremail/</link><description><![CDATA[
<p>Lots of people have been asking for a way for ProcessWire to support sending of email, outside of just using PHP's mail() function. I haven't really wanted to expand the scope of ProcessWire that deep into email sending, but I have been wanting to setup a way so that people could override how emails are sent, with modules. For people that are interested in making other ways of sending email in ProcessWire, I've setup a new module base class called <a data-ipb="nomediaparse" href="https://github.com/ryancramerdesign/ProcessWire/blob/dev/wire/core/WireMail.php" rel="external nofollow">WireMail</a>, and a new function called <a data-ipb="nomediaparse" href="https://github.com/ryancramerdesign/ProcessWire/blob/dev/wire/core/Functions.php#L545" rel="external nofollow">wireMail()</a>. The wireMail() function will use whatever WireMail module is installed. If none is installed, then it will use PW's default WireMail implementation (based on PHP's default mail function). </p>

<p>The wireMail() function replaces all instances of PHP's mail() function in ProcessWire's source. It works in a similar way as PHP's mail() except that supports a few <a data-ipb="nomediaparse" href="https://github.com/ryancramerdesign/ProcessWire/blob/dev/wire/core/Functions.php#L545" rel="external nofollow">different usages</a>. Standard usage would be this:</p>
<pre class="ipsCode prettyprint">
// to, from, subject, body
wireMail('user@domain.com', 'ryan@runs.pw', 'Mail Subject', 'Mail Body'); </pre>
<p>Another usage would be to give it no arguments, and it'll return whatever WireMail module is installed for you to use yourself. If no WireMail module is installed, then it returns ProcessWire's WireMail.</p>
<pre class="ipsCode prettyprint">
$mail = wireMail();
$mail-&gt;to('user@hi.com')-&gt;from('ryan@runs.pw'); // all calls can be chained
$mail-&gt;subject('Mail Subject'); 
$mail-&gt;body('Mail Body');
$mail-&gt;bodyHTML('&lt;html&gt;&lt;body&gt;&lt;h1&gt;Mail Body&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;'); 
$mail-&gt;send(); 
</pre>
<p>Since all of this stuff is only on the PW 2.4 dev branch at present, I'm posting this primarily for people that are interested in creating WireMail modules. For instance, I know that both Teppo and Horst (and perhaps others?) have put together such modules and ideas, so this is all aimed at coming up with a way for those ideas to be easily integrated into PW by way of modules. To make your own WireMail module, you simply create a module that extends WireMail and provide your own implementation for the send() method. Of course, you can go further than that, but that's all that is technically necessary. I've attached an example module called WireMailTest that demonstrates a WireMail module. When installed, it is used rather than PW's WireMail. This WireMailTest module includes lots of comments for you in the code of it, and you may find it helpful to use it as your starting point. </p>

<p><a href="https://processwire.com/talk/applications/core/interface/file/attachment.php?id=2546" data-fileid="2546" rel="">WireMailTest.module</a></p>

<p>For you guys developing modules, please throw any questions my way and I'm happy to help. Likewise, let me know if you think I'm missing anything important in the <a data-ipb="nomediaparse" href="https://github.com/ryancramerdesign/ProcessWire/blob/dev/wire/core/WireMailInterface.php" rel="external nofollow">base interface</a> that the modules are based upon and we can update it. </p>

]]></description><guid isPermaLink="false">5693</guid><pubDate>Sat, 01 Mar 2014 16:24:34 +0000</pubDate></item><item><title>Best hook for replacing page rendering?</title><link>https://processwire.com/talk/topic/4647-best-hook-for-replacing-page-rendering/</link><description><![CDATA[
<p>Hi folks,</p>

<p>I recently released the module <a href="http://modules.processwire.com/modules/template-twig-replace/" rel="external">Template Twig Replace</a> that enables templating using the Twig engine instead of the default php/html templates.</p>

<p>The module hooks into PageRender::___renderPage, replacing the original method and setting the Twig rendering results to the event's return property. I've taken a good amount of information from <a href="http://processwire.com/talk/topic/4212-cannot-hook-before-pagerender/?hl=%2Bhook+%2Bpage+%2Brender" rel="">this post</a> to achieve the results.</p>

<p>Now I've discovered that pages will not be cached if the module is active.</p>
<p>Some digging into the core (/wire/modules/PageRender.module) showed, that the method, I'm hooking not only renders a page but handles page caching (and some form of access control) as well.</p>

<p>As a quick fix I duplicated the code handling cache files in my module to ensure pages a properly cached as usual. The fix is already commited to <a href="https://github.com/marcostoll/processwire-template-twig-replace" rel="external nofollow">github</a> and should be available in the module directory soon.</p>

<p>This is would my module currently looks like:</p>

<pre class="ipsCode prettyprint">
public function init() {

    // ... some more stuff ..

    // replace default page rendering by hooked method
    $this-&gt;addHookBefore('PageRender::renderPage', $this, 'renderPageWithTwig');
}
</pre>

<pre class="ipsCode prettyprint">
/**
 * Hook callback for PageRender::renderPage
 *
 * Replaces default page rendering entirely.
 *
 * @param HookEvent $event The hook event
 * @throws WirePermissionException Page is not currently viewable.
 */
public function renderPageWithTwig(HookEvent $event) {

    $parentEvent = $event-&gt;arguments(0); // grab event provided to PageRender::renderPage

    // don't mess with admin templates
    $page = $parentEvent-&gt;object;
    if ($page-&gt;template == 'admin') return;

    // double check page's status
    // taken from PageRender::__render()
    if ($page-&gt;status &gt;= Page::statusUnpublished &amp;&amp; !$page-&gt;viewable()) {
        throw new WirePermissionException('Page \'' . $page-&gt;url . '\' is not currently viewable.');
    }

    // forced replacing of default page rendering behaviour
    $event-&gt;replace = true;

    // look for cached data
    // taken from PageRender::__render()
    $options = count($parentEvent-&gt;arguments) ? $parentEvent-&gt;arguments[0] : array();
    $defaultOptions = array(
        'forceBuildCache' =&gt; false,
    );
    $options = array_merge($defaultOptions, $options);

    $cacheAllowed = wire('modules')-&gt;get('PageRender')-&gt;isCacheAllowed($page);
    $cacheFile = null;

    if ($cacheAllowed) {
        $cacheFile = wire('modules')-&gt;get('PageRender')-&gt;getCacheFile($page);
        if(!$options['forceBuildCache'] &amp;&amp; ($data = $cacheFile-&gt;get()) !== false) {
            $parentEvent-&gt;return = $data;
            return;
        }
    }

    // allow page fields to be accessed directly in Twig
    // e.g. {{ page.myfield }} instead of {{ page.get('myfield') }}
    Page::$issetHas = true;

    // render template
    $twigVars = $this-&gt;collectVariables($page-&gt;output);
    $output = $this-&gt;getTwig()-&gt;render($page-&gt;template-&gt;name . '.' . 
                                       wire('config')-&gt;templateExtension, $twigVars);

    // cache output if possible
    // taken from PageRender::__render()
    if (!empty($output) &amp;&amp; $cacheAllowed &amp;&amp; !is_null($cacheFile)) $cacheFile-&gt;save($output);

    // manually set return of original event
    $parentEvent-&gt;return = $output;
}
</pre>

<p>But I think, the best option would be finding a hook the only renders a page's content and replacing this method.</p>
<p>Is there a better hook to use that leaves handling page caching and maybe other pre- or post-processing alone and does template rendering only?</p>

<p>Regards,</p>

<p>Marco</p>
]]></description><guid isPermaLink="false">4647</guid><pubDate>Wed, 02 Oct 2013 10:08:20 +0000</pubDate></item><item><title>Creating a module: Install/Unistall/Install gives strange error...</title><link>https://processwire.com/talk/topic/31514-creating-a-module-installunistallinstall-gives-strange-error/</link><description><![CDATA[<p>
	I am creating a module.<br />
	The first-time install works without error, and the module functions without error.<br />
	However, when I uninstall the module, then attempt to install the module again, I get the following error:<br />
	"Unable to install module [module]: Role collision detected ([role]): role already exists."<br />
	The role was deleted successfully, and no reference to that name exists in the database.<br />
	Has anyone come across this before?
</p>
]]></description><guid isPermaLink="false">31514</guid><pubDate>Sat, 25 Oct 2025 18:34:07 +0000</pubDate></item><item><title>Pending modules not visible in "My Modules" directory</title><link>https://processwire.com/talk/topic/31462-pending-modules-not-visible-in-my-modules-directory/</link><description><![CDATA[<p>
	
</p>

<p style="color:#000000;">
	Hi all,<br />
	I’ve recently published a module and it still shows as<span> </span><i>pending</i>. I can’t see it in the "My Modules" directory overview – although I can navigate to the module page directly via URL. <br />
	Two questions:
</p>

<ol start="1" style="color:#000000;">
	<li>
		<p>
			Is it intentional that even the author doesn’t see their own pending module in the overview?
		</p>
	</li>
	<li>
		<p>
			What’s the process for a module to move out of<span> </span><i>pending</i><span> </span>status? Does it require manual review or certain criteria?
		</p>
	</li>
</ol>

<p style="color:#000000;">
	Thanks for any insights!<br />
	Cheers, Mike
</p>
]]></description><guid isPermaLink="false">31462</guid><pubDate>Mon, 22 Sep 2025 11:14:31 +0000</pubDate></item><item><title>ProcessDataTables: The Swiss Army Knife for ProcessWire Data Visualization</title><link>https://processwire.com/talk/topic/31263-processdatatables-the-swiss-army-knife-for-processwire-data-visualization/</link><description><![CDATA[<p style="color:#000000;">
	Hey ProcessWire Community,
</p>

<p style="color:#000000;">
	I’m excited to announce a new module currently in<span> </span><span><b>beta</b></span>:<span> </span><span><b><a href="https://processwire.com/modules/process-data-tables/" rel="external">ProcessDataTables</a></b></span><span> </span>– designed as a universal “Swiss Army knife” for backend data visualization. Before the development of the <a href="https://processwire.com/modules/process-user-data-table/" rel="external">ProcessUserDataTable</a> module we developed a sleek module for a client called<span> </span><span><b>ProcessDataTable</b></span><span> </span>(singular), which featured the same guided, user-friendly install process with select/multiselect configuration fields but the possibility to handle the column display with small PHP templates. It was also designed for using it with a single table. But we saw the need for a truly flexible solution that can handle an unlimited number of tables in the ProcessWire admin.
</p>

<p style="color:#000000;">
	<span><b>ProcessDataTables</b></span><span> </span>(plural) is aimed at more experienced ProcessWire developers who need a rapid, highly configurable, and reusable tool for building complex data overviews and admin dashboards. Unlike the original (still unreleased) single-table module, which focuses on ease of setup, this new version prioritizes scalability, customization, and extensibility. We are currently using it in combination with <a contenteditable="false" data-ipshover="" data-ipshover-target="https://processwire.com/talk/profile/2137-bernhard/?do=hovercard" data-mentionid="2137" href="https://processwire.com/talk/profile/2137-bernhard/" rel="">@bernhard</a>s incredible module RockCommerce to display E-Commerce related data for backend users.
</p>

<h3 style="color:#000000;">
	Why ProcessDataTables?
</h3>

<ul style="color:#000000;">
	<li>
		<p>
			<span><b>Unlimited custom tables:</b></span><span> </span>Quickly define as many tables as you need for any data structure or ProcessWire template.
		</p>
	</li>
	<li>
		<p>
			<span><b>Advanced configuration:</b></span><span> </span>Control each table’s columns, data sources, selectors, and field-level output via PHP stubs.
		</p>
	</li>
	<li>
		<p>
			<span><b>Per-field templating:</b></span><span> </span>Every column can use its own output template for advanced formatting and presentation.
		</p>
	</li>
	<li>
		<p>
			<span><b>Global module config:</b></span><span> </span>Set date/currency/number formatting, labels, and output options from a central settings screen.
		</p>
	</li>
	<li>
		<p>
			<span><b>Built for developers:</b></span><span> </span>Configuration is more open and flexible, allowing you to create advanced dashboards and reporting interfaces for any ProcessWire installation.
		</p>
	</li>
</ul>

<h3 style="color:#000000;">
	Differences to the (yet unreleased) ProcessDataTable Module
</h3>

<p style="color:#000000;">
	The original<span> </span><span><b>ProcessDataTable</b></span><span> (singular) </span>module was focused on single-table use cases and offered a more beginner-friendly installation process:
</p>

<ul style="color:#000000;">
	<li>
		<p>
			<span><b>Guided setup:</b></span><span> </span>Selection of templates and fields through select/multiselect inputs in the config screen
		</p>
	</li>
	<li>
		<p>
			<span><b>Quicker onboarding:</b></span><span> </span>Suitable for smaller projects or less technical users when only one data table is needed
		</p>
	</li>
</ul>

<h3 style="color:#000000;">
	Status
</h3>

<ul style="color:#000000;">
	<li>
		<p>
			<span><b>ProcessDataTables</b></span><span> </span>is currently in<span> </span><span><b>beta</b></span><span> </span>– feedback, ideas, and bug reports are highly appreciated!
		</p>
	</li>
	<li>
		<p>
			Looking for real-world use cases and input from the community to help shape the roadmap.
		</p>
	</li>
</ul>

<h4 style="color:#000000;">
	Screenshots &amp; Example Workflow
</h4>

<p style="color:#000000;">
	In the README of the module you find example screenshots of the module config, adding tables, and customizing table columns)
</p>

<p>
	<strong>Get it here: </strong>
</p>

<p>
	<strong><a href="https://processwire.com/modules/process-data-tables/" rel="external">https://processwire.com/modules/process-data-tables/</a></strong>
</p>

<p>
	<strong><a href="https://github.com/frameless-at/ProcessDataTables" rel="external nofollow">https://github.com/frameless-at/ProcessDataTables</a></strong>
</p>

<hr />
<p style="color:#000000;">
	If you have ideas, feature requests, or want to help test or contribute, please reply below or DM me! <br />
	Looking forward to your feedback and suggestions!
</p>
]]></description><guid isPermaLink="false">31263</guid><pubDate>Fri, 23 May 2025 09:59:18 +0000</pubDate></item><item><title>[solved] How to limit access to a Process Setup page and still make the module accessible on the frontend?</title><link>https://processwire.com/talk/topic/31412-solved-how-to-limit-access-to-a-process-setup-page-and-still-make-the-module-accessible-on-the-frontend/</link><description><![CDATA[<p>
	I'm creating a module that has a configurable module (Admin-&gt;Modules) that installs a Process module page (Admin-&gt;Setup-&gt;My Module).
</p>

<p>
	Calling the configurable module from the frontend refers to the Process module page for data.
</p>

<p>
	I'd like to make the Admin-&gt;Setup-&gt;My Module page managed by module-specific permissions when logged-in to admin. All works while I'm logged in as Superuser but crashes with <br />
	<span style="background-color:#dc143c;color:#ffffff;font-size:16px;">Error: Exception: You do not have permission to execute this module - ProcessMyModule</span>
</p>

<p>
	when a frontend guest.
</p>

<p>
	Fairly confident it's in the mymodule.info.php permissions array. Just can't seem to nail it.
</p>

<p>
	Help appreciated.
</p>

<p>
	 
</p>

<p>
	<strong>Solution</strong>:<br />
	I created a role without any permissions. On the admin page I only allowed superusers or users with the specified role to view certain sections.
</p>

<p>
	<br />
	 
</p>

<p>
	 
</p>
]]></description><guid isPermaLink="false">31412</guid><pubDate>Thu, 14 Aug 2025 13:05:30 +0000</pubDate></item><item><title>A module for managing login and register pages?</title><link>https://processwire.com/talk/topic/31380-a-module-for-managing-login-and-register-pages/</link><description><![CDATA[<p>
	Hello, does it exist a module for managing login and register pages? 
</p>

<p>
	Or do I need to write the logic myself?
</p>

<p>
	Thank you very much
</p>
]]></description><guid isPermaLink="false">31380</guid><pubDate>Wed, 23 Jul 2025 12:58:30 +0000</pubDate></item><item><title>[solved] RockForms nette RadioList Control Renderer</title><link>https://processwire.com/talk/topic/31370-solved-rockforms-nette-radiolist-control-renderer/</link><description><![CDATA[<p>
	Hi PW Community,
</p>

<p>
	for a RockForms nette Form I need a custom Markup for the RadioList Control.
</p>

<p>
	addRadioList outputs the control per default as
</p>

<p>
	&lt;label&gt;&lt;Input type=radio /&gt; Text&lt;/label&gt;
</p>

<p>
	what I want to achieve:
</p>

<p>
	&lt;Input type=radio /&gt;&lt;label&gt;Text&lt;/label&gt;
</p>

<p>
	I can add classes and Separator Markup. But how can I inject a custom Markup?
</p>

<p>
	Any ideas?
</p>
]]></description><guid isPermaLink="false">31370</guid><pubDate>Sat, 19 Jul 2025 08:54:46 +0000</pubDate></item><item><title>[solved] - Image Upload not working with PHP 8.x</title><link>https://processwire.com/talk/topic/31340-solved-image-upload-not-working-with-php-8x/</link><description><![CDATA[<p>
	Hi, 
</p>

<p>
	even with the latest version of PW the image upload module does not working anymore.<br />
	When dragging in an image the spinner keeps spinning at 100% (1st sceenshot) and the console throws an ajax error (2nd screenshot).
</p>

<p>
	Not even with PHP 8.0 … latest working version is 7.4.x … how can this be? PHP 8.x is around for quiet a while now.
</p>

<p>
	 
</p>

<p><a href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2025_07/1848652456_Screenshot2025-07-04at2_46_15PM.png.803eba1faf7ba3d3360f5d7edd221494.png" class="ipsAttachLink ipsAttachLink_image" ><img data-fileid="28214" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2025_07/1848652456_Screenshot2025-07-04at2_46_15PM.png.803eba1faf7ba3d3360f5d7edd221494.png" data-ratio="96.99" width="166" class="ipsImage ipsImage_thumbnailed" alt="Screenshot 2025-07-04 at 2.46.15 PM.png"></a></p>
<p><a href="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2025_07/1518388198_Screenshot2025-07-04at2_46_10PM.png.447cde1498d48a78addd3483ed66fe13.png" class="ipsAttachLink ipsAttachLink_image" ><img data-fileid="28215" src="https://processwire.com/talk/applications/core/interface/js/spacer.png" data-src="//processwire-forums.s3.us-west-2.amazonaws.com/monthly_2025_07/1518388198_Screenshot2025-07-04at2_46_10PM.png.447cde1498d48a78addd3483ed66fe13.png" data-ratio="55.47" width="548" class="ipsImage ipsImage_thumbnailed" alt="Screenshot 2025-07-04 at 2.46.10 PM.png"></a></p>]]></description><guid isPermaLink="false">31340</guid><pubDate>Fri, 04 Jul 2025 12:54:16 +0000</pubDate></item></channel></rss>
