Jump to content

theoretic

Members
  • Posts

    110
  • Joined

  • Last visited

Posts posted by theoretic

  1. Thanks for your interest friends! After some attempts I decided to this the hard way. Here's my working code:

    $wire->addHookAfter("Pages::cloned", function($event) {
    
    	$clonePage = $event->arguments(1);
    	$clonePage->of(false);
    
    	$languages = \ProcessWire\wire('languages');
    	$emptyLanguageValues = [];
    	foreach($languages as $language) $emptyLanguageValues[$language->name] = '';
    
    	foreach( $clonePage->fields as $field ){
    
    		switch(true){
    
    			case $field->type instanceof \ProcessWire\FieldtypePageTitleLanguage:
    			case $field->type instanceof \ProcessWire\FieldtypeTextLanguage:
    			case $field->type instanceof \ProcessWire\FieldtypeTextareaLanguage:
    				$clonePage->{$field->name}->setLanguageValues($emptyLanguageValues);
    			break;
    
    			case $field->type instanceof \ProcessWire\FieldtypeInteger:
    			case $field->type instanceof \ProcessWire\FieldtypeFloat:
    				$clonePage->{$field->name} = NULL;
    			break;
    
    			case $field->type instanceof \ProcessWire\FieldtypeFile:
    			case $field->type instanceof \ProcessWire\FieldtypeImage:
    				$clonePage->$field->deleteAll();
    			break;
    
    			case $field->type instanceof \ProcessWire\FieldtypePage:
    				$clonePage->{$field->name}->removeAll();
    			break;
    		}
    
    	}
    
    	$clonePage->save();
    	$clonePage->of(true);
    
    });

    This example covers the most used types of data fields. It was enough for me, and it can be extended to include more data fields.

    By the way, I suppose that it could be a good idea to have a possibility to turn data copy on/off while cloning. But it's in PW core so I don't dare to change this.

     

  2. Hi friends! And thanks for ProcessWire!

    Content managers I work with like to create new pages by cloning the old ones. No bad in general, but they frequently forget to check and clean up all the content they copy, especially the multilanguage fields. So I decided to make a hook which should wipe all data fields of a recently created clone immediately after it has been created.

    Hooking such event is a piece of cake (thanks Processwire!) but cleaning all data fields appears to be a tricky business. Of course I've read this topic but it appears to be irrelevant in my case.

    My hook is basically like this:

    $wire->addHookAfter("Pages::cloned", function($event) {
    	$clonePage = $event->arguments(1);
    	$clonePage->of(false);
    	//$clonePage->fields = $clonePage->fields->makeNew(); //fails
    	// ...some more tricks including iterating through all data fields...
    	$clonePage->of(true);
    	$clonePage->save();
    }

    Will be grateful for any possible advice )

  3. On 7/30/2024 at 1:01 AM, ryan said:

    @theoretic

    It's actually the opposite of that. ProcessWire is an API and that's all it is. Any output comes from the user of the API and not from ProcessWire, as it doesn't output anything on its own. 

    ProcessWire started as a headless CMS, before the term even existed. The admin was later added on as an application built in PW, and then grew from there. But the base of PW is still just nothing but the API. ProcessWire itself doesn't output HTML. But since most use it for that, all of our example site profiles output HTML. There could just as easily be a site profile that outputs JSON or XML, or whatever you want it to. The point is PW has always been separate from whatever you choose to output with it, so that it has no opinion about what you use it for.  While we could have a site profile that outputs something other than HTML, I don't really know what we would have it output or who it would be for, but maybe someone else does (?), it would be a simple matter. 

    Thank You @ryan! Maybe I was slightly wrong. Of course PW is not about "returning html by default". It's a site profile thing, not a core thing. Making an API-dedicated site profile is a perfect idea. Or even an "API + headless CMS" profile. I have seen some API-dedicated PW modules, but, considering the PW architecture, it could be better to have API functionality at site profile level.

    • Like 1
  4. On 7/29/2024 at 5:57 PM, AndZyk said:

    Hello @theoretic,

    have you tried StaticWire?
    https://processwire.com/modules/static-wire/

    I have used it once for a locally hosted website for a exhibition, but it worked for me as a basic SSG.

    For online hosted websites I always use ProCache, which is AFAIK similar to a SSG :

    https://processwire.com/store/pro-cache/#procache-is-for-speed-and-seo

    Regards, Andreas

    I took a look at StaticWire. It's truely a basic SSG, but IMO it's rather a local tool not intended for use in hosting environment. It's okay if You have some hundreds of pages, but what if there's 1000+ of them?.. Generating a lot of static files at once can take too much memory and time, so personally I opted for a lazy static pages generation. Here's my module doing exactly this:

    https://github.com/theoretic/StaticPages

    It's open source, and it's free.

    • Like 6
  5. Hi friends! And thanks for ProcessWire!

    Maybe I'm a bit late to the party, but I'd also like to bring some ideas here. Processwire is a great CMS having many brilliant ideas inside. But there's always some space for further improvements.

    JAMStack paradigm

    It's about static html pages which have some javascript-driven parts interacting with some APIs and changing their look and feel depending on what users do and which data API provides. Sometimes there's just a single static html page which behaves as a multipage web application, sometimes there are just small dynamic chunks. This approach has many advantages:

    • There's no need to generate pages each time they are requested from server. They can be generated just once.
    • Returning static pages drastically reduces the server loading.
    • Static pages are safe by nature. There's nothing to hack there. And even if someone hacks into the server, there will be only some static html files there.
    • APIs return pure data, no html at all. The most typical API data format is JSON which is pretty compact and human readable already, and there are others (bson etc.) which are even more compact.

    Processwire vs JAMStack

    The current ProcessWire is pretty far from JAMStack paradigm:

    • By default, PW is about generating pages on each server request. And, being fair, lots of such pages could be static (generated only once).
    • If someone will take a closer look to how the actual PW admin works, he/she will find that there are ajax-driven things there. But in these cases PW generally returns the chunks of html markup, not pure data.
    • There's no native PW tools providing an easy way to build an API over PW. There are some 3rd part modules, and it's not a rocket science to build a PW-based API from scratch, but no native support out of the box.

    Processwire as "headless CMS"

    Headless CMS is a CMS which can only return data, not html. This approach is an important part of JAMStack approach, and there's plenty of such CMSs on the market. PW can be a perfect headless CMS, but it's not by default. You need to build an API on the top of PW to make it "headless". I did this in some of my projects, and I'm quite happy with the result. But, come on, give me a reason why this functionality doesn't deserve to be in the PW core? )

    Processwire as "static site generator" (SSG)

    SSGs are another important part of JAMStack world. They are tools allowing You to make static html files based on the data and the templates You have. Contrary to headless CMSs, SSGs are generally the tools used locally, and the result (static pages) is later uploaded to server. It's quite interesting that PW can be a server-based SSG! I also did this, and I was quite satisfied again. But AFAIK there's no core-level or even 3rd party tools to do this using PW.

    Bringing the pieces together

    PW can make a big step from the ol' good paradigm of server-side page rendering to the modern JAMStack. Two things are missing:

    • Native tools for building an API on top of PW. Again, it's doable, but it's not native.
    • A PW-compatible (maybe even PW-based) SSG which won't be a headache.

    Both things are not rocket science, and they potentially could bring much more attention to PW, make it interesting for younger devels. I had a lot of talk like "ahhh You're that guy from 90ties, still writing php code and making server-rendered pages? You're the past already, man!".

    Hope this will worth a good discussion ?

    • Like 5
  6. Hi friends! And thanks for Processwire!

    Maybe I'm asking a noob question but I couldn't find any clear answer here. Is it possible to paste content from a doc / docx / odt file to a wysiwyg textarea field and to have all images automatically saved to a dedicated image field? Did some experiments, it appears that this may work somehow on MacOS with docx files. But it's definintely not working on Windows, any wysiwyg, any browser.

    Guess I'm not the only one having this question. Any advice is welcome )

  7. Hi friends! Many thanks for this brilliant module!

    It appears I've found a heavy issue concerning php 8.3.7 . In brief: Fluency crashes, giving multiple errors of this kind:

    Uncaught Error: Undefined constant "Fluency\Engines\DeepL\CURLOPT_CONNECTTIMEOUT" in site/modules/Fluency/app/Engines/DeepL/DeepLEngine.php:220

    Digging inside reveals the following code sample:

        $requestConfig = [
          CURLOPT_CONNECTTIMEOUT => 5,
    ...

    CURLOPT_CONNECTTIMEOUT looks here like a constant, however it's not. PHP 8.3.4 can however handle this kind of arrays, and Fluency works like a charm. But it's not the case for PHP 8.3.7 .

    I think it's worth taking this into consideration during further development. I've also created an issue on the Github module page.

    Hope this will help to make this module even better ?

  8. I've found the solution. Hope this will be helpful to someone )

    //options for AsmSelect should be (int)key=>value pairs
    $options = [ 1=>'A value', 15=>'Another value', 200=>'And even more', ];
    
    //default values should be array of keys, not values
    $defaultValues = [ 15, 200, ];
    
    //options can also be an array of (int)keys
    $otherOptions = [ 25, 35, 45 ];
    
    ...
    
    $config = [
    
    			[
    				'name'					=> 'myField',
    				'type'					=> 'AsmSelect',
    				'label'					=> $this->_('My field'),
    				'options'				=> $options,
    				'value'					=> $defaultValues,
    			],
    ...
    ];

     

  9. On 12/12/2023 at 5:20 PM, da² said:

    Hey,

    I see your idea like having static properties, instances are pages and static properties are on template.
    I don't know it this is a good idea, on my side, since I discovered custom page classes I declare this on UserPage for example:

    public function getAvatar(): string
    {
        return $this->avatar ?: wire()->config->urls->templates . 'img/default-avatar.png';
    }

    I could also have the default avatar on a generic configuration page.

    Thank You @da²! Both ways are usable. But I still think it's a bit "patchy", especially hardcoding the paths/URLs of the default images. Having a "regular" image inputfield allowing to upload images, add some metadata to them etc. could be a much better solution. That's why I had the idea of creating the second set of per-template fields.

  10. Hi fellas! And thanks for Processwire!

    Couldn't find myself an answer to a question which appears to be not too complex. I'm developing a configurable module which uses a MyModule.config.php file to store its config fields. I'd like to use the AsmSelect fieldtype with some (maybe dynamic) options there. Basically it's dead simple:

    $config = [
    
    			[
    				'name'					=> 'MyFieldName',
    				'type'					=> 'AsmSelect',
    				'label'					=> $this->_('My config field'),
    			],
    ...
    ];

    This code displays the AsmSelect, but I have no idea how to add options there. Tried some staff like this:

    $config = [
    
    			[
    				'name'					=> 'MyFieldName',
    				'type'					=> 'AsmSelect',
    				'label'					=> $this->_('My config field'),
    				'options'				=> [
    					'name1'						=> 'value1',
    				],
    			],
    ...
    ];

    , but couldn't get any result.

    I know that it's possible to define config inputfields right inside the .module file, but I'd like to separate the config from module itself (and the MyModule.config.php is a perfect way to do so). So dear Santa, do You have any idea on how to add some options to my inputfield? Thanks in advance )

  11. Hi fellas! And thanks for Processwire!

    I have an idea which, I suppose, might appear interesting to the PW community. I'd call it "template fields".

    Come on, we have it already! Any template may have fields, it's Processwire basics. Yes. And no! The fields attached to any template are used with the pages having this template, but not with the template itself.

    Feel perplexed? A short pseudocode example to explain what I'm trying to say.

    //one-of-my-templates.php
    ...
    foreach( $productPages as $productPage ){
    	$productImage = $productPage->image? : $productPage->template->image;
    	//outputting some html etc.
    }
    ...

    Default field values (e.g. default images) are not a strong point of Processwire. Okay, we can use 3rd-party modules like @Macrura's Settings Factory (but oops, SF doesn't support image fields). Or we can create a dedicated "defaults" page with dedicated "defaults" template having all necessary fields... hmm, looks like a patch, yes? At last, we can give up and use some hardcoded image urls (yes, it's "patchy" again!).

    Using a kind of $page->template->image could be a much better solution. Let's imagine a second set of data fields bound to any template. The first one represents the fields used like $page->myfield. The second one could be used in the following way: $page->template->myfield or even $template->myfield. This approach may give us much more flexibility than PW already has, and I don't suppose it should be too complex to implement. Maybe it will be reasonable to make a module offering this new functionality. Think I possibly could do this... but would like to see the community reaction first.

    So what do You think friend? Is it worth doing or not? Thanks in advance for any opinion.

  12. Hi friends! And thanks for Processwire )

    Maybe I'm missing something, but it appears that PW doesn't have a dedicated selector for text length.

    Possible selector:

    $selectedUsers = $users->find("name.length<=5,sort=name.length");

    Expected output:

    Bob
    Andy
    Maria

    ( Steven and Andrea will be skipped because that names are too long )

    Of course it's possible to filter the find() results using some custom function, but having a native selector instead could be a better practice.

    So does that silver bullet really exist? If no, I think it could be useful to many happy PW developers )

  13. Hi friends! And thanks for ProcessWire!

    There's an inconvenience in ProcessWire file translation process which, I think, deserves a topic here. It's a common case to have same translatable phrases in different translatable files. Using a translation context (in fact, a separate file holding all that phrases) is also a common case, at least for me. Generally i call that context I18N_GLOBAL. So my typical translation may look like this:

    <?=__( 'Accept', I18N_GLOBAL )?>

    The problem is that ProcessWire translation interface is context-agnostic. If You have to translate multiple files containing the same 'Accept'-related piece of code, it will be always treated as not translated yet, even if I18N_GLOBAL file is already translated. Quite annoying, especially if You have lots of phrases to translate.

    So dear Santa, could You please make translation interface context-aware and to mark all the phrases (in all the files) which already have translation in my I18N_GLOBAL file as translated? Or maybe I missed something, and there's a possibility to tweak some settings and achieve this?

    Thanks in advance for any possible advice or idea )

    • Like 1
  14. Hi friends! And thanks for Processwire!

    While supporting a 5-years-old Processwire project I found that there are both InnoDB and MyISAM tables there. Personally I think that InnoDB is better for Processwire. MyISAM tables came from some modules written years before. Since I like websites to be fast, I altered all MyISAM tables to InnoDB. At first glance, it was a good solution, website is running fast and smooth. Could there be caveats? Maybe I'm taking some implicit risks? I Would like to discuss this.
     

    • Like 1
  15. Hi friends! And thanks for Processwire!

    I have an interesting question. Couldn't find an answer myself.

    Let's suppose we have a hook of this kind:

    $wire->addHookAfter("Pages::saveReady", function($event) {
    	$user = $event->arguments(0);
    	//some manipulations with user data
    }

    This hook can be triggered both by frontend (there are some forms there allowing users to edit their profiles) and by backend (when superuser saves user page).
    I'd like this hook to have different behavior in these two cases. My first guess was like this:

    $wire->addHookAfter("Pages::saveReady", function($event) {
    	$user = $event->arguments(0);
    	$page = event->object;
    	if( $page->template == 'admin' ):
    		//some admin-specific manipulations with user data
    }

    No way! $page is always null, no matter how the hook was triggered.
    Any idea how to guess which page has triggered the hook?
    Thanks in advance for possible help ?
     

  16. On 10/18/2022 at 12:24 AM, AndreasWeinzierl said:

    I have tried updating from 3.x to 3.0.200, however I get the following error in my log:

    2022-10-17 19:51:04	?		Fatal Error:  
    Uncaught Error: Class "FieldtypeMulti" not found in site/modules/FieldtypeTemplates/FieldtypeTemplates.module:30 
    
    Stack trace: 
    #0 /wire/core/Modules.php(1686): include_once() 
    #1 /wire/core/Modules.php(1605): ProcessWire\Modules->includeModuleFile() 
    #2 /wire/core/WireClassLoader.php(284): ProcessWire\Modules->includeModule() #3 [internal function]: ProcessWire\WireClassLoader->loadClass() 
    #4 /wire/core/Modules.php(2739): class_exists() 
    #5 /wire/core/Modules.php(3023): ProcessWire\Modules->getModuleInfoInternal() 
    #6 /wire/core/Modules.php(1106): ProcessWire\Modules->getModuleInfo() 
    #7 wire/core/Modules.php(913): ProcessWire\Modules->loadModule() 
    #8 /wire/core/Modules.php(389): ProcessWire\Modules->load() 
    #9 /wire/core/ProcessWire.php(536): ProcessWire\Modules->init() 
    #10 /wire/core/ProcessWire.php(314): ProcessWire\ProcessWire->load() 
    #11 /index.php(52): ProcessWire\ProcessWire->__construct() #12 {main}   thrown (line 30 of /site/modules/FieldtypeTemplates/FieldtypeTemplates.module)

    Any ideas?

     

    Hi @AndreasWeinzierl! Got into same trouble. Fortunatelty, it can be fixed quite easily. Just uncomment

    //namespace ProcessWire;

    at the very beginning of the module. Have no guess why @kixe had commented it. Works fine after uncommenting, at least for me )  

    • Like 1
  17. Hi friends! And thanks for Processwire!

    I have an interesting question concerning selectors. Let's suppose we have an "inventory" template with "owner" repeater holding all users who owned this inventory. The last repeater item represents the current owner. Is there any possibility to select an inventory page by some data contained in its last owner repeater item? Some pseudocode to clarify what i want:

    //getting inventory recently owned by different users by currently owned by current user
    
    $myInventoryPage = $pages->get("template=inventory,owners(last).id={$user->id}");

    Using owners.id is not an option here because the "owner" repeater may contain the current user in any position, not the last one. Thanks in advance for any idea!

  18. Hi friends! And thanks for Processwire!

    Maybe i'm missing something, but i have no idea how to select an (admin) page by the process it uses. It could look this:

    "template=admin,process=my_process_name"

    This could be a great help while using a perfect ProcessSettingsFactory module (many thanks to @Macrura!). That module allows to create admin settings pages, each holding a certain set of configurable data fields. Getting that fields is a piece of cake:

    $settings = $settingsFactory->getSettings('general'); //'general' is the name of admin page holding the settings
    $phone = $settings->phone;

    Works great when we need quite a limited number of settings. But when the things are getting bigger, it becomes quite reasonable to create a number of settings pages and to organize all the settings into a unified structure. Here's an example how it could be:

    //getting all settings pages
    $settingsPages = $pages->find("template=admin,process=ProcessSettingsFactory");
    
    //creating the settings object
    $settings = (Object)[];
    
    //putting all data from settings pages into $settings object
    foreach( $settingsPages as $settingsPage ){
    	$settingName = $settingsPage->name; //e.g. 'general'
    	$settings->$settingName = $settingsFactory->getSettings($settingName);
    }
    
    //now we can use the $settings object
    
    echo "<a href=mailto:{$settings->general->email}>Mail us!</a>";

    This approach definitely requires a process-specific selector. Guess that it could also be implemented at module level but having a specific selector for process could be useful in a large number of tasks.

    Will appreciate any idea )

×
×
  • Create New...