Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by dragan

  1. On 7/22/2018 at 4:58 PM, ryan said:

    the possibility to reset passwords and disabling 2FA for a user

    There's already a password reset module built into PW. 2FA can be disabled for any individual account as needed. 

    I have a PW installation, where I activated 2FA for myself as superuser. Recently, my phone for some reason deleted several apps - one of them being Google Authenticator. I know how to reset a user password, but that won't help me in that particular situation.


    Is there an API method to deactivate 2FA for a certain user? In the docs, I only see $user->hasTfa().

  2. @picarica Could you please use a more civilized language in here? I'm no PW forum admin, but I'm pretty sure the F*** word is not welcome here.

    If you have a problem with that particular module, you should post your question in the respective support forum instead: 


    • Like 4
  3. 1 hour ago, rash said:

    The big plan behind would lead a bit too far

    I don't think so... the trick with PW hooks is to find out which hook to use, and whether to use before or after. Page::loaded seems to be the wrong method to hook into for what you want to achieve.

    Here are some code-bits I used in the past, and they all work as expected (inside site/ready.php)

        $this->addHookAfter('Pages::saveReady', function (HookEvent $event) {
            $page = $event->arguments[0];
    // ----------
        $pages->addHookAfter("ProcessPageEdit::buildForm", function (HookEvent $event) {
            $page = $event->object->getPage();
            $form = $event->return;
    // ----------
    $this->addHookBefore('Pages::saved(template=invoice, parent=23444)', function(HookEvent $event) {
        $page = $event->arguments(0);


    • Like 4
  4. That's where a proper IDE should kick in, to do its stuff (auto-suggest/auto-complete etc.). Mere text-editors can only do so much...

    With PW, that didn't really work so well for a long time. But if you enable the Functions API, and use page() instead of $page in your code, you'll get the hints in your working environment.

     * Allow core API variables to also be accessed as functions?
     * Recommended. This enables API varibles like $pages to also be accessed as pages(),
     * as an example. And so on for most other core variables.
     * Benefits are better type hinting, always in scope, and potentially shorter API calls.
     * See the file /wire/core/FunctionsAPI.php for details on these functions.
     * @var bool
    $config->useFunctionsAPI = true;


    • Like 1
  5. 24 minutes ago, dragan said:

    Well, insert debug code (Tracy's d($var) or plain print_r() etc.) to see if any of your variables/functions doesn't do what you think it should.

    Did you do any of that? Do you have Tracy Debugger installed? Do you have debug mode enabled? Did you check if you have any hooks activated that could block your code? Saying "it doesn't work" without trying to debug each line of code makes it difficult to find the culprit, and for those people trying to help you. We don't even know your PW version, or what modules you have installed.

    For starters, what happens if you simplify the code and enter a few lines directly in the Tracy console (e.g. just edit the bio field). Does it work or not?

  6. 33 minutes ago, modifiedcontent said:

    and it still doesn't work

    Well, insert debug code (Tracy's d($var) or plain print_r() etc.) to see if any of your variables/functions doesn't do what you think it should.

    body and fullname are certainly fields you have added to the user profile yourself, they're not part of the default user template. Perhaps you need to check those fields in the user template where it says "What fields can a user edit in their own profile?"

  7. 14 hours ago, modifiedcontent said:

    most basic working example

    Well, I just tested out this on PW 3.0.167, and it works as expected:

    $user_roles       = user()->roles;
    $user_roles_count = count($user_roles);
    // make sure the current user has more than just default "guest" role
    if (($user_roles_count > 1) && !input()->post->ts) {
        $u_name = user()->name;
        $u_id   = user()->id;
        $here   = page()->url;
        $ts     = time();
        $form = <<<EOF
    <h1>Hello $u_name . Would you like to change your password?</h1>
    <form method="post" action="$here">
        <label for="new_pass">New password:    
            <input type="password" id="new_pass" name="new_pass" autocomplete="false">
        <label for="new_pass2">Confirm new password:
            <input type="password" id="new_pass2" name="new_pass2" autocomplete="false">
        <input type="hidden" id="uid" name="uid" autocomplete="false" value="$u_id">
        <input type="hidden" id="uname" name="uname" autocomplete="false" value="$u_name">
        <input type="hidden" id="ts" name="ts" autocomplete="false" value="$ts">
        <button type="submit">Change your password</button>    
        echo $form;
    if (input()->post->ts && input()->post->new_pass && input()->post->new_pass2 && input()->post->uid) {
        $out        = '';
        $errCount   = 0;
        $form_uid   = input()->post->uid;
        $form_uname = input()->post->uname;
        $curr_uid   = user()->id;
        $curr_uname = user()->name;
        $form_new_pass  = input()->post->new_pass;
        $form_new_pass2 = input()->post->new_pass2;
        $form_ts        = (int)input()->post->ts;
        $max_time       = 30 * 60; // 30 minutes
        // If the POST arrives a half hour later, something is probably fishy
        if ($form_ts < (time() - $max_time)) {
            $out = "It seems that it took you longer than 30 minutes to change your password - your request has been blocked due to security concerns. Please try again.";
        // user has maybe manipulated the hidden form input fields
        if (($curr_uid != $form_uid) || ($curr_uname != $form_uname)) {
            $out = "Your request has been blocked due to security concerns. Please try again.";
        // for added security, I would highly suggest to take this note seriously @ https://cheatsheet.processwire.com/user/user-properties/user-pass/
        // "you will need to add your own complexity checks for passwords to ensure they meet a minimum length and character requirement."
        // I'm not sure if there is an API sanitizer method available for this purpose
        // below is just a basic check to ensure both pw-field values are identical
        if (($form_new_pass !== $form_new_pass2)) {
            $out = "Your new passwords do not match. Please try again.";
        // If we've got that far, we're ready to update the user's PW
        if ($errCount < 1) {
            $user_page = users()->get($curr_uid);
            $user_page->pass = $form_new_pass;
            $out = "Success! Your account has been updated with the new password you've chosen.";
        echo $out;

    If syntax like user()->roles doesn't work in your installation, you can either enable it in site/config.php ($config->useFunctionsAPI = true;) or re-write it with $user / $page / $input notation.

    • Like 2
  8. First things I'd do:

    • check PW logs
    • check server error logs (Apache / PHP)
    • inspect in the browser (JS errors)

    If you don't find any hints that way, you could see if it's a CSRF problem, or something to do with sessions. Switching to SessionHandlerDB can help (a core module). In one of my setups I had to put these two config values to false, when I encountered similar problems:

    $config->protectCSRF = false;
    $config->sessionFingerprint = false;


    • Like 1
  9. It's weird that mySQL Workbench doesn't show any INNODB_FT_DEFAULT_STOPWORD table, just these three:


    That's with version 5.7.24 though. It seems strange that I don't see that table, but PW search works just fine.

    You could try to use the Migration Wizard from Workbench (with a local copy), perhaps you'll get more control with your import. But ultimately, since this is a system table, maybe you don't have enough rights to manipulate that anyway on your hosting setup.

  10. You can go to site/assets/files/1234/ and send these JSON files to the translators. 1234 being the id of the alternative language.

    You'll see the language id @ setup/languages/ when you hover over the language edit link (1010 seems to be the default language).

    Before that, you'll want to make sure all those JSONs are there, so you'll need to click the "find files to translate" button @ setup/languages/edit/?id=10189 under "Site Translation Files". There's also two buttons there in the bottom-right for downloading all files as CSV or ZIP. When you get the translated JSON files back, you can upload them here as well, and replace the old ones.

    • Like 1
  11. 8 hours ago, psy said:

    retrieves qty of items

    Can you elaborate on this? Is the quantity derived from a page / selector count? And that's all you really need with all those queries? There are faster ways to get counts than to always return the full page arrays. (I probably miss the "big picture", sorry if it's a dumb question)

  12. Variables like $page are reserved by PW, so you shouldn't try to redefine them. PW tries to find a field 'autores', which is not available in the current $page, but in the page array you're looping through. Try:

    foreach($pages as $p) {
    	if(count($p->autores)) {

    If you install Tracy Debugger (which every PW developer should, imho), you can include debug output for variables, which is a big help when developing.

    • Like 1
  13. 8 hours ago, bilioso said:
    15 hours ago, dragan said:

    a relational ID that relates from PW to the old Drupal site


    The concept is actually quite simple: A PW field (integer), which you create and use in all templates that you need for the Drupal import. That field can just as well be hidden in the PW admin.

    In your export CSV you have to make sure that the unique node ID is present, and then you can match those two fields.

    If you don't export/import the published date in your initial migration step already, you can just as well update it later:

    $drupalNodes = array(1 => 1601804655, 2 => 1601804674, 5 => 1601804676);
    foreach($drupalNodes as $k=>$v) {
        $p = $pages->findOne("drupal_id=$k");
        $p->setAndSave('published', $v);


  14. Hi @bilioso and welcome to the PW forum.

    You can edit the publish date programmatically or via module.

    Given your situation, it would be far more efficient to do it via the PW API.

    If you've imported your Drupal pages to PW already, I hope you also defined and populated a relational ID that relates from PW to the old Drupal site.

    If you did that, it's just a couple of lines to update the publish date with the PW API in one go. Let us know if you need instructions (but I guess the first link I posted should give you enough clues)

    Disclaimer: I haven't used the module I linked. Although compatibility with PW v. 3.x is not claimed, chances are it will still be working fine with the latest PW version. If not, maybe all that's needed is to add PW namespace in the very 1st line of the module.

  15. Is there a reason that PW's core internal search (ProcessPageSearch) doesn't support the newly added "advanced search" method? i.e. the one described at the bottom here #= / with the ability to use * - +.

    Did @ryan simply forgot to add it in the module config screen, or maybe because of potential performance bottlenecks?

    Just discovered this in the module code:

    	 * Get operators used for searches, where key is operator and value is description
    	 * @return array
    	static public function getOperators() {
    		$operators = Selectors::getOperators(array(
    			'getIndexType' => 'operator',	
    			'getValueType' => 'label',
    		unset($operators['#=']); // maybe later
    		return $operators;


  16. 3 hours ago, Roych said:

    Thank you dragan but still not working, same error again.

    Well, you can still create a page in your source PW installation that outputs either a JSON of your n latest articles, or complete with markup. On the other PW installation, just fetch that page in some way and include it in your template file, which can be as easy as file_get_contents('http://pw-site-with-articles.com.test/latest-articles.php'); No need to install and configure a full-fledged REST API module for that.

    • Like 5
  17. @Roych fwiw - I've got this little test setup running fine on my local dev machine (Windows with Laragon and dummy .tld .test enabled)

    <?php namespace ProcessWire;
    // this runs in a folder "etc", which can be accessed locally @ http://etc.test/fetcharticles.php (no PW in this folder)
    // I guess it could just as well be in the PW root of the instance from where you fetch the articles
    require_once 'D:/laragon/www/pwbig/index.php'; // source PW installation root index.php
    $out = '';
    $site_1 = new ProcessWire('D:/laragon/www/pwbig/', 'http://pwbig.test/'); // source path / URL
    $site_1_articles = $site_1->pages->get('/articles')->children;
    $out .= "<div class='articles'>";
    foreach($site_1_articles as $article) {
        $title = $article->title;
        $out .= "<a href='{$article->httpUrl}' title='$title'>"; // httpUrl instead of url
        $out .= "<div class='article'>";
        $out .= "<h2>$title</h2>";
        if($article->images->count) {
            $thumb = $article->images->first;
            $myImg = $thumb->size(200, 200);
            $out .= "<figure><img src='{$myImg->httpUrl}' alt='{$myImg->description}'></figure>"; // httpUrl instead of url
        $out .= "</div></a>";
    $out .= "</div>";
    echo $out;


  • Create New...