We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 32098
    • 31 Posts
    I am trying to modify the Register snippet so that visitors can just register a username and email address to sign up for a newsletter and have themselves entered into a MODx Revolution usergroup (called ’mailing’). So far I have got the registration bit working and the names and emails are popping up nicely in the usergroup. Now for the client I need to create a snippet that will retrieve the username and emails of users in that group (only) as a comma-separated list so that they can save that on their PC and use it in a (non-MODx)bulk mailing system.

    Does anyone have any ideas how to retrieve the usernames and emails in that particular usergroup without creating too many gaping security weaknesses? (The setup is MODx Revolution 2.0.8)

    Thanks.
      • 32098
      • 31 Posts
      In the absence of Bob Ray’s book helping those of us who still shake at the sound of the letters XPDO I have had to make do with a nasty old-fashioned procedural solution to get the Register snippet to let me run a little system to store usernames and emails for a newsletter registration system and then let me retrieve those two bits of data as a csv file.

      Q: How? A: Simply commenting out the sections in Register that require the password and check for the repetition of the username (because it doesn’t matter if 50 people on the mailing list want to be called George). That leaves the Register snippet simply registering a username and the email.

      Then to retrieve the two bits of data as a csv file use this snippet (it needs to get the username from a table called modx_users and the email from a second table called modx_user_attributes):

      <?php
      require_once('../mysqli_connect.php');
      
      $query = "SELECT modx_users.username, modx_user_attributes.email FROM modx_users INNER JOIN modx_user_attributes ON modx_users.id = modx_user_attributes.internalKey";
      $values = mysqli_query($dbc, $query);
      if($values) {
      //$num = mysqli_num_rows($values) ;
      //echo '<p>There are '.$num.' rows for the mailing list.</p>';
      $csv_output = '';
      while ($row = mysqli_fetch_row($values)) {
      
      $csv_output .= $row[0].", ".$row[1]."\n";
      
      }}
      echo $csv_output;
      $file="mailinglist";
      $filename = $file."_".date("Y-m-d_H-i",time());
      header("Content-type: application/vnd.ms-excel");
      header("Content-disposition: csv" . date("Y-m-d") . ".csv");
      header( "Content-disposition: filename=".$filename.".csv");
      print $csv_output;
      exit;
      mysqli_close($dbc);
      ?>


      Works, but is admittedly nasty.
        • 3749
        • 24,544 Posts
        My solution would be equally nasty. I’m not good at xPDO joins -- and that’s beyond the scope of my book. wink

        I would add something like this at the top:

        if (! $modx->user->isMember('mailing')) {
        
            return ('Sorry, you're not a member of the group');
           // or use $modx->sendRedirect() to send them somewhere else
        
        }


        Otherwise, anyone can visit the page and get the list.

        Here is the very inefficient (but more readable) non-join method with xPDO:

        <?php
        $csv_output = '';
        
        $group = $modx->getObject('modUserGroup', array ('name'=>'mailing'));
        $users = $group->getMany('Users');
        
        foreach ($users as $user) {
        
            $profile = $user->getOne('Profile');
        
            $csv_output .= $user->get('username') . ", " . $profile->get('email') . "\n";
        }
        
        /* same code as above */
          Did I help you? Buy me a beer
          Get my Book: MODX:The Official Guide
          MODX info for everyone: http://bobsguides.com/modx.html
          My MODX Extras
          Bob's Guides is now hosted at A2 MODX Hosting
          • 32098
          • 31 Posts
          Bob, Thanks for the help. By the way, I love the lightheartedness behind the expression "something like this" - it brought a smile to my face.

          Let me begin by emphasizing the Historical Need for your book. I surely cannot be alone. I worked my way diligently through Larry Ullman’s "PHP6 and MySQL5" and felt at the end of it that I really had learnt something. Now that I look at the stuff of which MODx is made I feel as if I know nothing. To try to sort out some problems I was having with the bit of code you posted I went to have a look at the API. That is only for the initiated, though. Hieroglyphics to the rest of us. We need a book that explains the relevant OOP principles in a practical context.

          Anyway, I tried your first bit of code to close the gaping security hole and only give logged-in people access to the csv file. To do that I created a resource group containing only the relevant resource, then created a user group called mailmanager containing me as the sole user and the csv resource as the sole resource. Tried to make sure the relevant access and context permissions were correct.

          Then set up a front end login page that would redirect to the page with the csv-generating snippet call in its content area.

          This was the tricky bit of the snippet (you suggested ’user’ but I wondered after looking at the API if that should be ’modUser’):

          if ($modx->modUser->isMember('mailmanager')) {
          /* Code to generate csv file */
          } else {
              return ('Sorry, you are not authorised to access this resource');
          }
          


          That gave me the following fatal error: Fatal error: Call to a member function isMember() on a non-object.

          Tried 5 or 6 variations on that theme using various things found in the API (like isAuthenticated or getUserGroups using that first to get the user groups for that particular user before checking membership). All generated fatal errors because of some non-object.

          Where can a guy turn at times like these? Bob Ray’s FAQs, that’s where. And I find the following:

          How can I tell if a user is logged in? 
          In a snippet, the API function $modx->userLoggedIn() returns true if the user is logged in.
          


          So I used that at the top of my snippet and it works (and I am assuming that the security hole is now sufficiently plugged).

          <?php
          if ($modx->userLoggedIn()){
          
          require_once('../mysqli_connect.php');
          
          $query = "SELECT modx_users.username, modx_user_attributes.email FROM modx_users INNER JOIN modx_user_attributes ON modx_users.id = modx_user_attributes.internalKey";
          $values = mysqli_query($dbc, $query);
          if($values) {
          
          $csv_output = '';
          while ($row = mysqli_fetch_row($values)) {
          
          $csv_output .= $row[0].", ".$row[1]."\n";
          
          }}
          
          $file = "mailinglist";
          $filename = $file."_".date("Y-m-d_H-i",time());
          header("Content-type: application/vnd.ms-excel");
          header("Content-disposition: csv" . date("Y-m-d") . ".csv");
          header( "Content-disposition: filename=".$filename.".csv");
          print $csv_output;
          mysqli_close($dbc);
          exit;
          } else {
              return ('Sorry, you are not authorised to access this resource');
          }
          ?>


          Couldn’t get the second bit of code you offered to work, but, not to worry, I am happy with what I have got so far.

          Thanks.
            • 3749
            • 24,544 Posts
            I put "something like this" because I didn’t have time to test the code. wink

            It’s definitely $modx->user->isMember(). MODX automatically sets $modx->user on every page request to point to the currently logged-in user (or (anonymous) if no one is logged in). I’m not sure what else could be wrong. [update] I see it now, missing parenthesis -- I seem to do that a lot when I don’t have my editor to check things. It should be:

            if (! $modx->user->isMember('mailmanager') ){  // extra parenthesis at the end of this line
            
                return ('Sorry, you're not a member of the group');
               // or use $modx->sendRedirect() to send them somewhere else
            
            }
            


            BTW, another easy test (I think a little faster) is:

            if ($modx->user->get('username') == '(anonymous)') {
              /* user is not logged in */
               return "";
            }


            Note that if you are previewing from the Manager, you will always be logged in.

            The only drawback of your code (and the code just above) is that it doesn’t check to see if the user is a member of any particular user group. In your case, that may not matter, but it does make things a tiny bit less secure.
              Did I help you? Buy me a beer
              Get my Book: MODX:The Official Guide
              MODX info for everyone: http://bobsguides.com/modx.html
              My MODX Extras
              Bob's Guides is now hosted at A2 MODX Hosting